diff --git a/CMakeLists.txt b/CMakeLists.txt index c91030a4b89e3cc148a25a4ce699dfdefd7b1e23..ca9ce75f71c23c8d481cb85a675189111d78fa96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0) add_compile_options(-Wno-error=unused-but-set-variable) add_compile_options(-Wno-error=unused-variable) add_compile_options(-Wno-error=unused-parameter) - add_compile_options(-Wno-error=deprecated) + add_compile_options(-Wno-error=deprecated-declarations) endif() ####################################################################################### @@ -89,12 +89,13 @@ OPTION(Simox_USE_OPENSCENEGRAPH_VISUALIZATION "Use OpenSceneGraph for visualizat OPTION(Simox_COLLISION_CHECKER_PQP "Build and use the PQP Collision Detection library (located in ExternalDependencies)" ON) ####################################################################################### MESSAGE(STATUS "******************** Configuring Simox ************************") +set (Simox_BUILD_VirtualRobot TRUE CACHE BOOL "Build Virtual Robot library") set (Simox_BUILD_Saba TRUE CACHE BOOL "Build Motion Planning library") set (Simox_BUILD_GraspStudio TRUE CACHE BOOL "Build Grasp Planning library") set (Simox_BUILD_SimDynamics TRUE CACHE BOOL "Build Dynamic Simulation") MESSAGE (STATUS "** SIMOX BUILD SimoxUtility: TRUE (can't be turned off)") -MESSAGE (STATUS "** SIMOX BUILD VirtualRobot: TRUE (can't be turned off)") +MESSAGE (STATUS "** SIMOX BUILD VirtualRobot: ${Simox_BUILD_VirtualRobot}") MESSAGE (STATUS "** SIMOX BUILD Saba : ${Simox_BUILD_Saba}") MESSAGE (STATUS "** SIMOX BUILD GraspStudio : ${Simox_BUILD_GraspStudio}") MESSAGE (STATUS "** SIMOX BUILD SimDynamics : ${Simox_BUILD_SimDynamics}") @@ -194,13 +195,15 @@ endif() ####################################################################################### add_subdirectory(SimoxUtility) -add_subdirectory(VirtualRobot) - +list (APPEND SIMOX_EXPORT_TARGET_LIST SimoxUtility) +list (APPEND Simox_LIBRARIES SimoxUtility) ####################################################################################### -list (APPEND SIMOX_EXPORT_TARGET_LIST SimoxUtility VirtualRobot) -list (APPEND Simox_LIBRARIES SimoxUtility VirtualRobot) - +if (Simox_BUILD_VirtualRobot) + add_subdirectory(VirtualRobot) + list(APPEND SIMOX_EXPORT_TARGET_LIST VirtualRobot) + list(APPEND Simox_LIBRARIES VirtualRobot) +endif() ####################################################################################### if (Simox_BUILD_Saba) diff --git a/SimoxUtility/CMakeLists.txt b/SimoxUtility/CMakeLists.txt index 22721c174cc9a07a91168d4199c475ed15a3c126..cbbe5d613e9bb03097b69119beffc4291a9dbf5d 100644 --- a/SimoxUtility/CMakeLists.txt +++ b/SimoxUtility/CMakeLists.txt @@ -50,6 +50,8 @@ MESSAGE (STATUS "\n***** CONFIGURING Simox project SimoxUtility *****") SET(SOURCES SimoxUtility.cpp + algorithm/string/string_tools.cpp + color/cmaps/colormaps.cpp color/cmaps/Named.cpp color/Color.cpp @@ -89,6 +91,9 @@ SET(INCLUDES algorithm/for_each_if.h algorithm/apply.hpp algorithm/get_map_keys_values.h + algorithm/string/string_tools.h + algorithm/string/string_conversion.h + algorithm/string/string_conversion_eigen.h color/cmaps/colormaps.h color/cmaps/Named.h @@ -96,6 +101,7 @@ SET(INCLUDES color/Color.h color/ColorMap.h color/GlasbeyLUT.h + color/KellyLUT.h color/hsv.h color/interpolation.h color/json.h @@ -106,6 +112,7 @@ SET(INCLUDES error/SimoxError.h + filesystem/make_relative.h filesystem/list_directory.h filesystem/remove_trailing_separator.h @@ -216,6 +223,11 @@ SET(INCLUDES meta/enum/adapt_enum.h meta/enum/EnumNames.hpp + xml/rapidxml/rapidxml.hpp + xml/rapidxml/rapidxml_print.hpp + xml/rapidxml/RapidXMLWrapper.h + xml/rapidxml/RapidXMLForwardDecl.h + shapes/AxisAlignedBoundingBox.h shapes/OrientedBoxBase.h shapes/OrientedBox.h diff --git a/SimoxUtility/algorithm.h b/SimoxUtility/algorithm.h index 10719420d53a3b1e3873d1a93a6b08b2210e3019..53e55e3b48d89ff17a21649b83f055116f6699c2 100644 --- a/SimoxUtility/algorithm.h +++ b/SimoxUtility/algorithm.h @@ -4,3 +4,4 @@ #include "algorithm/for_each_if.h" #include "algorithm/get_map_keys_values.h" +#include "algorithm/string.h" diff --git a/SimoxUtility/algorithm/string.h b/SimoxUtility/algorithm/string.h new file mode 100644 index 0000000000000000000000000000000000000000..30178e410dcaf690eac928eaacabff26d658112a --- /dev/null +++ b/SimoxUtility/algorithm/string.h @@ -0,0 +1,7 @@ +#pragma once + +// This file is generated! + +#include "string/string_conversion.h" +#include "string/string_conversion_eigen.h" +#include "string/string_tools.h" diff --git a/SimoxUtility/algorithm/string/string_conversion.h b/SimoxUtility/algorithm/string/string_conversion.h new file mode 100644 index 0000000000000000000000000000000000000000..857abdf9480d2c8efac498c2552695ac32fe924b --- /dev/null +++ b/SimoxUtility/algorithm/string/string_conversion.h @@ -0,0 +1,143 @@ +/** + * This file is part of Simox. + * + * Simox is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Simox is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Andre Meixner (andre dot meixner at kit dot edu) + * @copyright 2020 Andre Meixner + * GNU Lesser General Public License + */ + +#pragma once + +#include <type_traits> +#include <iomanip> + +#include "SimoxUtility/error.h" +#include "string_tools.h" + +namespace simox::alg { + namespace help { + template <typename T> + struct type{}; + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + inline T to_(const std::string& s, type<T>, std::locale locale = DEFAULT_LOCALE, bool trim = true) + { + char c; + std::stringstream ss; + ss.imbue(locale); + if (trim) + ss << alg::trim_copy(s); + else + ss << s; + T value; + ss >> value; + if (ss.fail() || ss.get(c)) { + throw error::SimoxError("Cannot convert string " + s + " to " + typeid(T).name()); + ss.clear(); + } + return value; + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + inline T to_(const std::string& s, type<bool>, std::locale /*locale*/, bool trim = true) + { + std::string help = to_lower(s); + if (trim) alg::trim(help); + if (help == "true" || help == "1") return true; + else if (help == "false" || help == "0") return false; + else throw error::SimoxError("Cannot convert string " + s + " to boolean."); + } + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + inline T to_(const std::string& s, std::locale locale = DEFAULT_LOCALE, bool trim = true) + { + return help::to_<T>(s, help::type<T>{}, locale, trim); + } + + template<typename T, typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr> + inline std::vector<T> to_vec(const std::vector<std::string>& vec, std::locale locale = DEFAULT_LOCALE, bool trimElements = false) + { + std::vector<T> res; + for (const std::string& str : vec) + { + res.push_back(to_<T>(str, locale, trimElements)); + } + return res; + } + + template<typename T, typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr> + inline std::vector<T> to_vec(const std::string& str, const std::string& splitBy = "\t ", bool trimElements = true, + bool ignoreEmptyElements = true, std::locale locale = DEFAULT_LOCALE) + { + return to_vec<T>(split(str, splitBy, trimElements, ignoreEmptyElements, locale), locale, false); + } + + template<typename T, typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr> + inline std::vector<T> to_vec_check_size(const std::string& str, unsigned int expectedSize, const std::string& splitBy = "\t ", + bool trimElements = true, bool ignoreEmptyElements = true, std::locale locale = DEFAULT_LOCALE) + { + return to_vec<T>(split_check_size(str, expectedSize, splitBy, trimElements, ignoreEmptyElements, locale), locale, false); + } + + + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + inline std::string to_string(T x, const std::locale& locale = DEFAULT_LOCALE) + { + std::stringstream ss; + ss.imbue(locale); + ss << x; + return ss.str(); + } + + template<> inline std::string to_string<double>(double d, const std::locale& locale) + { + std::stringstream ss; + ss.imbue(locale); + ss << std::setprecision(12) << d; + return ss.str(); + } + + template<> inline std::string to_string<bool>(bool x, const std::locale& /*locale*/) + { + return x ? "true" : "false"; + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + inline std::string to_string(const std::vector<T>& vec, const std::string& splitBy = " ", std::locale locale = DEFAULT_LOCALE) + { + std::stringstream stream; + stream.imbue(locale); + for(unsigned int i = 0; i < vec.size(); ++i) { + stream << to_string(vec.at(i)); + if (i + 1 != vec.size()) stream << splitBy; + } + return stream.str(); + } + + template<typename T, typename std::enable_if<!std::is_fundamental<T>::value>::type* = nullptr> + inline std::string to_string(const std::vector<T>& vec, const std::string& splitBy = " ", std::locale locale = DEFAULT_LOCALE) + { + std::stringstream stream; + stream.imbue(locale); + for(unsigned int i = 0; i < vec.size(); ++i) { + stream << vec.at(i); + if (i + 1 != vec.size()) stream << splitBy; + } + return stream.str(); + } +} diff --git a/SimoxUtility/algorithm/string/string_conversion_eigen.h b/SimoxUtility/algorithm/string/string_conversion_eigen.h new file mode 100644 index 0000000000000000000000000000000000000000..864fa7f236bfdf68710694fac886d2dbcfb26c7e --- /dev/null +++ b/SimoxUtility/algorithm/string/string_conversion_eigen.h @@ -0,0 +1,74 @@ +/** + * This file is part of Simox. + * + * Simox is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Simox is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Andre Meixner (andre dot meixner at kit dot edu) + * @copyright 2020 Andre Meixner + * GNU Lesser General Public License + */ + +#pragma once + +#include <Eigen/Core> + +#include "string_conversion.h" + +namespace simox::alg { + template<typename T, int Rows = Eigen::Dynamic, typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr> + inline Eigen::Matrix<T, Rows, 1> to_eigen(const std::vector<std::string>& vec, std::locale locale = DEFAULT_LOCALE, bool trimElements = false) + { + if (Rows != Eigen::Dynamic && Rows != (int)vec.size()) + throw error::SimoxError("Sizes do not match!"); + Eigen::Matrix<T, Rows, 1> res(vec.size()); + for (unsigned int i = 0; i < vec.size(); i++) + { + res(i) = to_<T>(vec[i], locale, trimElements); + } + return res; + } + + template<typename T = float, typename std::enable_if<std::is_scalar<T>::value>::type* = nullptr> + inline Eigen::Matrix<T, Eigen::Dynamic, 1> to_eigen_vec(const std::string& str, const std::string& splitBy = "\t ", + bool trimElements = true, bool ignoreEmptyElements = true, std::locale locale = DEFAULT_LOCALE) + { + return to_eigen<T>(split(str, splitBy, trimElements, ignoreEmptyElements, locale), locale, false); + } + + template<typename T = float, typename std::enable_if<std::is_scalar<T>::value>::type* = nullptr> + inline Eigen::Matrix<T, Eigen::Dynamic, 1> to_eigen_vec_check_rows(const std::string& str, int expectedRows, const std::string& splitBy = "\t ", + bool trimElements = true, bool ignoreEmptyElements = true, std::locale locale = DEFAULT_LOCALE) + { + return to_eigen<T>(split_check_size(str, expectedRows, splitBy, trimElements, ignoreEmptyElements, locale), locale, false); + } + + template<typename T, unsigned int Rows, typename std::enable_if<std::is_scalar<T>::value>::type* = nullptr> + inline Eigen::Matrix<T, Rows, 1> to_eigen_vec(const std::string& str, const std::string& splitBy = "\t ", + bool trimElements = true, bool ignoreEmptyElements = true, std::locale locale = DEFAULT_LOCALE) + { + return to_eigen<T, Rows>(split_check_size(str, Rows, splitBy, trimElements, ignoreEmptyElements, locale), locale, false); + } + + template<typename T, int Rows, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr> + inline std::string to_string(const Eigen::Matrix<T, Rows, 1>& vec, const std::string& splitBy = " ", std::locale locale = DEFAULT_LOCALE) + { + std::ostringstream stream; + stream.imbue(locale); + for(unsigned int i = 0; i < vec.rows(); ++i) { + stream << to_string(vec(i)); + if (i + 1 != vec.rows()) stream << splitBy; + } + return stream.str(); + } +} diff --git a/SimoxUtility/algorithm/string/string_tools.cpp b/SimoxUtility/algorithm/string/string_tools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24f6ccf47fa4647880304cc49734108feefe64c0 --- /dev/null +++ b/SimoxUtility/algorithm/string/string_tools.cpp @@ -0,0 +1,79 @@ +#include "string_tools.h" + +#include "SimoxUtility/error/SimoxError.h" + +#include <algorithm> + +#include <iomanip> +#include <boost/algorithm/string.hpp> +#include <boost/bind.hpp> + +namespace simox::alg +{ + +std::string to_lower(const std::string& str) +{ + std::string res = str; + std::transform(res.begin(), res.end(), res.begin(), tolower); + return res; +} + +void trim(std::string& str, const std::locale& locale) { + boost::trim(str, locale); +} + +std::string trim_copy(const std::string& str, const std::locale& locale) { + return boost::trim_copy(str, locale); +} + +void trim_if(std::string& str, const std::string& trim) { + boost::trim_if(str, boost::is_any_of(trim)); +} + +std::string trim_copy_if(std::string& str, const std::string& trim) { + return boost::trim_copy_if(str, boost::is_any_of(trim)); +} + +std::vector<std::string> split(const std::string& str, const std::string& splitBy, bool trimElements, bool removeEmptyElements, const std::locale& locale) +{ + std::vector<std::string> strs; + boost::split(strs, str, boost::is_any_of(splitBy)); + + if (trimElements) std::for_each(strs.begin(), strs.end(), boost::bind(&boost::trim<std::string>, _1, locale)); + + if (removeEmptyElements) strs.erase(std::remove_if(strs.begin(), strs.end(), [](const std::string& s) { return s.empty(); }), strs.end()); + + return strs; +} + +std::vector<std::string> split_check_size(const std::string& str, unsigned int expectedSize, const std::string& splitBy, bool trimElements, bool removeEmptyElements, const std::locale& locale) +{ + std::vector<std::string> strs = split(str, splitBy, trimElements, removeEmptyElements, locale); + + if (strs.size() != expectedSize) + throw error::SimoxError("String " + str + " contains " + std::to_string(strs.size()) + " instead of " + std::to_string(expectedSize) + " values seperated by delimiter " + splitBy); + return strs; +} + +std::string join(const std::vector<std::string> vec, const std::string& delimiter, bool trimElements, bool ignoreEmptyElements, const std::locale& locale) { + std::ostringstream ss; + ss.imbue(locale); + for (size_t index = 0; index < vec.size(); index++) + { + if (trimElements) + { + std::string trimmed = trim_copy(vec.at(index), locale); + if (ignoreEmptyElements && trimmed.empty()) continue; + ss << trimmed; + } + else + { + if (ignoreEmptyElements && vec.at(index).empty()) continue; + ss << vec.at(index); + } + if (index != vec.size()) ss << delimiter; + } + return ss.str(); +} + +} diff --git a/SimoxUtility/algorithm/string/string_tools.h b/SimoxUtility/algorithm/string/string_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..d9df585aac83274a47a1eb210c140fb88be3f474 --- /dev/null +++ b/SimoxUtility/algorithm/string/string_tools.h @@ -0,0 +1,50 @@ +/** + * This file is part of Simox. + * + * Simox is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Simox is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Andre Meixner (andre dot meixner at kit dot edu) + * @copyright 2020 Andre Meixner + * GNU Lesser General Public License + */ + +#pragma once + +#include <string> +#include <vector> +#include <locale> + +namespace simox::alg { + static std::locale DEFAULT_LOCALE = std::locale::classic(); + + std::string to_lower(const std::string& str); + + void trim(std::string& str, const std::locale& locale = DEFAULT_LOCALE); + + std::string trim_copy(const std::string& str, const std::locale& locale = DEFAULT_LOCALE); + + void trim_if(std::string& str, const std::string& trim = "\t "); + + std::string trim_copy_if(const std::string& str, const std::string& trim = "\t "); + + std::vector<std::string> split(const std::string& str, const std::string& splitBy = "\t ", bool trimElements = true, + bool removeEmptyElements = true, const std::locale& locale = DEFAULT_LOCALE); + + //! @param expectedSize throws SimoxError if split not matches expectedSize + std::vector<std::string> split_check_size(const std::string& str, unsigned int expectedSize, const std::string& splitBy = "\t ", + bool trimElements = true, bool removeEmptyElements = true, const std::locale& locale = DEFAULT_LOCALE); + + std::string join(const std::vector<std::string> vec, const std::string& delimiter = " ", bool trimElements = false, + bool ignoreEmptyElements = false, const std::locale& locale = DEFAULT_LOCALE); +} diff --git a/SimoxUtility/color.h b/SimoxUtility/color.h index 8504b2340383555e7cff98673094e90e8cbb233a..fa1290568667f9b447914b158226a0ddfb10d484 100644 --- a/SimoxUtility/color.h +++ b/SimoxUtility/color.h @@ -5,6 +5,7 @@ #include "color/Color.h" #include "color/ColorMap.h" #include "color/GlasbeyLUT.h" +#include "color/KellyLUT.h" #include "color/cmaps.h" #include "color/hsv.h" #include "color/interpolation.h" diff --git a/SimoxUtility/color/KellyLUT.h b/SimoxUtility/color/KellyLUT.h new file mode 100644 index 0000000000000000000000000000000000000000..f82f218a176709014bdd3f08497f3df81432bb8a --- /dev/null +++ b/SimoxUtility/color/KellyLUT.h @@ -0,0 +1,237 @@ +/* +This file is part of MMM. + +MMM is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +MMM is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with MMM. If not, see <http://www.gnu.org/licenses/>. +* +* @package MMM +* @author Andre Meixner +* @copyright 2020 High Performance Humanoid Technologies (H2T, 255; Karlsruhe, Germany +* +*/ + +#pragma once + +#include "Color.h" +#include <vector> +#include <set> + +namespace simox::color +{ + +/** + * @brief 20 distinguishable colors by Kenneth L. Kelly but without black and white, e.g. usable for creating graphs + */ +class KellyLUT { +public: + static const inline Color kelly_vivid_yellow = Color(0xFF, 0xB3, 0x00, 255); + static const inline Color kelly_strong_purple = Color(0x80, 0x3E, 0x75, 255); + static const inline Color kelly_vivid_orange = Color(0xFF, 0x68, 0x00, 255); + static const inline Color kelly_very_light_blue = Color(0xA6, 0xBD, 0xD7, 255); + static const inline Color kelly_vivid_red = Color(0xC1, 0x00, 0x20, 255); + static const inline Color kelly_grayish_yellow = Color(0xCE, 0xA2, 0x62, 255); + static const inline Color kelly_medium_gray = Color(0x81, 0x70, 0x66, 255); + + //The following will not be good for people with defective color vision + static const inline Color kelly_vivid_green = Color(0x00, 0x7D, 0x34, 255); + static const inline Color kelly_strong_purplish_pink = Color(0xF6, 0x76, 0x8E, 255); + static const inline Color kelly_strong_blue = Color(0x00, 0x53, 0x8A, 255); + static const inline Color kelly_yellowish_pink = Color(0xFF, 0x7A, 0x5C, 255); + static const inline Color kelly_strong_violet = Color(0x53, 0x37, 0x7A, 255); + static const inline Color kelly_vivid_orange_yellow = Color(0xFF, 0x8E, 0x00, 255); + static const inline Color kelly_strong_purplish_red = Color(0xB3, 0x28, 0x51, 255); + static const inline Color kelly_vivid_greenish_yellow = Color(0xF4, 0xC8, 0x00, 255); + static const inline Color kelly_strong_reddish_brown = Color(0x7F, 0x18, 0x0D, 255); + static const inline Color kelly_vivid_yellowish_green = Color(0x93, 0xAA, 0x00, 255); + static const inline Color kelly_deep_yellowish_brown = Color(0x59, 0x33, 0x15, 255); + static const inline Color kelly_vivid_reddish_orange = Color(0xF1, 0x3A, 0x13, 255); + static const inline Color kelly_dark_olive_green = Color(0x23, 0x2C, 0x16, 255); + + static const inline std::vector<Color> KELLY_COLORS_COLOR_BLIND = + { + kelly_vivid_yellow, kelly_strong_purple, kelly_vivid_orange, kelly_very_light_blue, kelly_vivid_red, kelly_grayish_yellow, kelly_medium_gray + }; + + static const inline std::vector<Color> KELLY_COLORS = + { + kelly_vivid_yellow, kelly_strong_purple, kelly_vivid_orange, kelly_very_light_blue, kelly_vivid_red, kelly_grayish_yellow, kelly_medium_gray, + kelly_vivid_green, kelly_strong_purplish_pink, kelly_strong_blue, kelly_strong_violet, kelly_strong_violet, kelly_vivid_orange_yellow, + kelly_strong_purplish_red, kelly_vivid_greenish_yellow, kelly_strong_reddish_brown, kelly_vivid_yellowish_green, kelly_deep_yellowish_brown, + kelly_vivid_reddish_orange, kelly_dark_olive_green + }; + + /** + * @brief Get a color from the lookup table with given ID. + * The ID is automaticall wrapped if greater than `size()`. + */ + static Color at(std::size_t id, int alpha = 255, bool color_blind = false) + { + id = id % size(color_blind); + return data(color_blind).at(id).with_alpha(alpha); + } + + /** + * @brief Get a color from the lookup table with given ID (with float values). + * The ID is automaticall wrapped if greater than `size()`. + */ + static Eigen::Vector4f atf(std::size_t id, float alpha = 1.f, bool color_blind = false) + { + return at(id, to_byte(alpha), color_blind).to_vector4f(); + } + + /** + * @brief Get a color from the lookup table with given ID. + * The ID is automaticall wrapped if greater than `size()`. + */ + template <typename UIntT, std::enable_if_t<std::is_unsigned_v<UIntT>, int> = 0> + static Color at(UIntT id, int alpha = 255, bool color_blind = false) + { + return at(static_cast<std::size_t>(id), alpha, color_blind); + } + /** + * @brief Get a color from the lookup table with given ID. + * The ID is automaticall wrapped if greater than `size()`. + * If `id` is negative, its absolute value is used. + */ + template <typename IntT, std::enable_if_t<std::is_signed_v<IntT>, int> = 0> + static Color at(IntT id, int alpha = 255, bool color_blind = false) + { + return at(static_cast<std::size_t>(id >= 0 ? id : std::abs(id)), alpha, color_blind); + } + + /** + * @brief Get a color from the lookup table with given ID (with float values). + * The ID is automaticall wrapped if greater than `size()`. + */ + template <typename UIntT, std::enable_if_t<std::is_unsigned_v<UIntT>, int> = 0> + static Eigen::Vector4f atf(UIntT id, float alpha = 1.f, bool color_blind = false) + { + return atf(static_cast<std::size_t>(id), alpha, color_blind); + } + + /** + * @brief Get a color from the lookup table with given ID (with float values). + * The ID is automaticall wrapped if greater than `size()`. + * If `id` is negative, its absolute value is used. + */ + template <typename IntT, std::enable_if_t<std::is_signed_v<IntT>, int> = 0> + static Eigen::Vector4f atf(IntT id, float alpha = 1.f, bool color_blind = false) + { + return atf(static_cast<std::size_t>(id >= 0 ? id : std::abs(id)), alpha, color_blind); + } + + /// Get the number of colors in the lookup table.; + static std::size_t size(bool color_blind = false) + { + return data(color_blind).size(); + } + + static std::vector<Color> data(bool color_blind = false) + { + return color_blind ? KELLY_COLORS_COLOR_BLIND : KELLY_COLORS; + } +}; + + +/** + * @brief An RGBA color, where each component is a byte in [0, 255]. Additionally contains an id denoting its position + */ +struct KellyColor : public Color +{ + KellyColor(size_t id = -1) : KellyColor(Color(), id) + { + } + + KellyColor(uint8_t r, uint8_t g, uint8_t b, size_t id) : Color(r,g,b), id(id) + { + } + + KellyColor(Color color, size_t id) : KellyColor(color.r, color.g, color.b, id) + { + } + + bool operator <(const KellyColor &other) const + { + return id < other.id; + } + + size_t id; +}; + +/** + * @brief Stack of 20 distinguishable colors by Kenneth L. Kelly but without black and white, e.g. usable for creating graphs + */ +class KellyColorStack { +public: + /** + * @brief Initializes a Stack of colors + * @param color_blind 7 instead of 20 colors for visually impaired + */ + KellyColorStack(bool color_blind = false) : + colors(createColorStack(KellyLUT::data(color_blind))) + { + } + + /** + * @brief Returns the next Kelly color and removes it from the stack, black color if stack is empty + * @return The next color from the top of the stack + */ + KellyColor next() + { + if (!colors.empty()) + { + auto it = colors.begin(); + auto color = *it; + colors.erase(it); + return color; + } + else return KellyColor(); + } + + /** + * @brief Puts a color back on the stack depending on its id + */ + void putBack(KellyColor color) + { + colors.insert(color); + } + + /** + * @return Amount of colors left on the stack + */ + std::size_t left() + { + return colors.size(); + } + + bool empty() + { + return colors.empty(); + } + +private: + inline std::set<KellyColor> createColorStack(const std::vector<Color> &colors) + { + std::set<KellyColor> set; + size_t index = 0; + for (const Color &color : colors) + { + set.insert(KellyColor(color, index++)); + } + return set; + } + + std::set<KellyColor> colors; +}; + +} diff --git a/SimoxUtility/filesystem.h b/SimoxUtility/filesystem.h index f2b3aaed65813fe2b2d258f01fc02e7c4f4d3f8e..d0e959b73b4e553aba2dfca9c3cf92b72c2cdacf 100644 --- a/SimoxUtility/filesystem.h +++ b/SimoxUtility/filesystem.h @@ -3,4 +3,5 @@ // This file is generated! #include "filesystem/list_directory.h" +#include "filesystem/make_relative.h" #include "filesystem/remove_trailing_separator.h" diff --git a/SimoxUtility/filesystem/make_relative.h b/SimoxUtility/filesystem/make_relative.h new file mode 100644 index 0000000000000000000000000000000000000000..598dc34b2d9bcaed721baf7ba53288e8c5bdf015 --- /dev/null +++ b/SimoxUtility/filesystem/make_relative.h @@ -0,0 +1,28 @@ +#pragma once + +#include <filesystem> + +namespace simox::fs +{ + //! Return path when appended to a_From will resolve to same as a_To + inline std::filesystem::path make_relative(std::filesystem::path a_From, std::filesystem::path a_To) + { + a_From = std::filesystem::absolute(a_From); + a_To = std::filesystem::absolute(a_To); + std::filesystem::path ret; + std::filesystem::path::const_iterator itrFrom(a_From.begin()), itrTo(a_To.begin()); + // Find common base + for (std::filesystem::path::const_iterator toEnd(a_To.end()), fromEnd(a_From.end()); itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo); + // Navigate backwards in directory to reach previously found base + for (std::filesystem::path::const_iterator fromEnd(a_From.end()); itrFrom != fromEnd; ++itrFrom) + { + if ((*itrFrom) != ".") + ret /= ".."; + } + // Now navigate down the directory branch + //ret.append(itrTo, a_To.end()); + for (; itrTo != a_To.end(); ++itrTo) + ret /= *itrTo; + return ret; + } +} diff --git a/SimoxUtility/rapidxml.h b/SimoxUtility/rapidxml.h new file mode 100644 index 0000000000000000000000000000000000000000..61180c2ee919dcc628678727b7ab22e95946c7f6 --- /dev/null +++ b/SimoxUtility/rapidxml.h @@ -0,0 +1,5 @@ +#pragma once + +// This file is generated! + +#include "rapidxml/RapidXMLWrapper.h" diff --git a/SimoxUtility/tests/CMakeLists.txt b/SimoxUtility/tests/CMakeLists.txt index 81bd1537a5df8485d5f3816ff845d4418cb0bd13..fd1c6cf4ae7e327280d3826ed943696bc085222a 100644 --- a/SimoxUtility/tests/CMakeLists.txt +++ b/SimoxUtility/tests/CMakeLists.txt @@ -1,4 +1,3 @@ - SET(TEST_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) MACRO(ADD_SU_TEST TEST_NAME) @@ -19,6 +18,7 @@ MACRO(ADD_SU_TEST TEST_NAME) ADD_EXECUTABLE(${TRG_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.cpp) TARGET_LINK_LIBRARIES(${TRG_NAME} PRIVATE SimoxUtility Boost::unit_test_framework) + target_include_directories(${TRG_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/..) if (NOT Boost_USE_STATIC_LIBS) target_compile_definitions(${TRG_NAME} PRIVATE -DBOOST_TEST_DYN_LINK) endif () diff --git a/SimoxUtility/tests/algorithm/CMakeLists.txt b/SimoxUtility/tests/algorithm/CMakeLists.txt index c08b9a3bdfb57968c8cfe0b6739ad8fab0701f06..353a58f6ca25f06577d53dd6e9eb5b2cdee533b7 100644 --- a/SimoxUtility/tests/algorithm/CMakeLists.txt +++ b/SimoxUtility/tests/algorithm/CMakeLists.txt @@ -1,4 +1,5 @@ ADD_SU_TEST( apply ) ADD_SU_TEST( for_each_if ) +ADD_SU_TEST( string_tools ) diff --git a/SimoxUtility/tests/algorithm/string_tools.cpp b/SimoxUtility/tests/algorithm/string_tools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a7cfda50d16d2ab2146030579b03225efc13d37 --- /dev/null +++ b/SimoxUtility/tests/algorithm/string_tools.cpp @@ -0,0 +1,86 @@ +/** +* @package SimoxUtility +* @author Andre Meixner +* @copyright 2020 Andre Meixner +*/ + +#define BOOST_TEST_MODULE SimoxUtility/algorithm/string_tools + +#include <boost/test/included/unit_test.hpp> + +#include <SimoxUtility/algorithm/string.h> + +BOOST_AUTO_TEST_CASE(to_lower) +{ + BOOST_CHECK_EQUAL(simox::alg::to_lower("TO LoWER tesT!"), "to lower test!"); +} + +BOOST_AUTO_TEST_CASE(split) +{ + std::string testString = " test 20 50 30 "; + std::string testString2 = " test ; 20,50 ; 30 "; + std::vector<std::string> expectedResult = { "test", "20", "50", "30" }; + BOOST_CHECK(simox::alg::split(testString) == expectedResult); + BOOST_CHECK(simox::alg::split(testString2, ";,") == expectedResult); + BOOST_CHECK_THROW(simox::alg::split_check_size(testString, 3), simox::error::SimoxError); +} + +BOOST_AUTO_TEST_CASE(to_eigen) +{ + std::string testString = " 5.0 3 -1.0 "; + Eigen::Vector3f expectedResult(5.0, 3.0, -1.0); + BOOST_CHECK(simox::alg::to_eigen_vec(testString) == expectedResult); + BOOST_CHECK((simox::alg::to_eigen_vec<float, 3>(testString)) == expectedResult); + BOOST_CHECK_THROW((simox::alg::to_eigen_vec<float, 4>(testString)), simox::error::SimoxError); +} + +BOOST_AUTO_TEST_CASE(to_string) +{ + bool testBool = false; + float testFloat = -3.156; + double testDouble = -3.34919295196; + Eigen::Vector3f testVector3f(5.0, 3.5, -1.0); + Eigen::VectorXf testVectorXf(9); + testVectorXf << 5.0, 3.5, -1.0, 15, 20, 10, 30, 100, 1; + std::vector<float> testVector = {5.0, 3.5, -1.0}; + std::vector<int> testVectorI = {1, -2, 3}; + std::vector<std::string> testVectorStr = {"Hello", "World!"}; + BOOST_CHECK_EQUAL(simox::alg::to_string(testBool), "false"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testFloat), "-3.156"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testDouble), "-3.34919295196"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testVector3f), "5 3.5 -1"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testVectorXf), "5 3.5 -1 15 20 10 30 100 1"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testVector), "5 3.5 -1"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testVectorI, ";"), "1;-2;3"); + BOOST_CHECK_EQUAL(simox::alg::to_string(testVectorStr), "Hello World!"); + +} + +BOOST_AUTO_TEST_CASE(from_string) +{ + std::string testBool = " 0"; + std::string testBool2 = "FALSE"; + std::string testFloat = " -3.156"; + std::string testVector = " 5 3.5 -1.0 "; + std::string testVector2 = " 5;3.5;-1.0 "; + std::string testVector3 = " 5 ,3.5 ,-1 ,15 ,20, 10,30,100,1 "; + bool expectedBool = false; + float expectedFloat = -3.156f; + Eigen::Vector3f expectedVector3f(5.0, 3.5, -1.0); + Eigen::VectorXd expectedVectorXd(9); + expectedVectorXd << 5.0, 3.5, -1.0, 15, 20, 10, 30, 100, 1; + Eigen::VectorXd expectedVector9d(9); + expectedVector9d << 5.0, 3.5, -1.0, 15, 20, 10, 30, 100, 1; + std::vector<float> expectedVector = {5.0, 3.5, -1.0}; + BOOST_CHECK_EQUAL(simox::alg::to_<bool>(testBool), expectedBool); + BOOST_CHECK_EQUAL(simox::alg::to_<bool>(testBool2), expectedBool); + BOOST_CHECK_EQUAL(simox::alg::to_<float>(testFloat), expectedFloat); + BOOST_CHECK(simox::alg::to_vec<float>(testVector) == expectedVector); + BOOST_CHECK(simox::alg::to_vec_check_size<float>(testVector2, 3, ";") == expectedVector); + BOOST_CHECK((simox::alg::to_eigen_vec<float, 3>(testVector)) == expectedVector3f); + BOOST_CHECK(simox::alg::to_eigen_vec(testVector2, ";") == expectedVector3f); + BOOST_CHECK(simox::alg::to_eigen_vec<double>(testVector3, ",") == expectedVectorXd); + BOOST_CHECK((simox::alg::to_eigen_vec_check_rows<double>(testVector3, 9, ",")) == expectedVectorXd); + BOOST_CHECK_THROW((simox::alg::to_eigen_vec_check_rows<double>(testVector3, 10, ",")), simox::error::SimoxError); + BOOST_CHECK((simox::alg::to_eigen_vec<double, 9>(testVector3, ",")) == expectedVector9d); +} diff --git a/SimoxUtility/tests/filesystem/CMakeLists.txt b/SimoxUtility/tests/filesystem/CMakeLists.txt index 4d7dc75427dd2bde914e18c9a865f93eafd9dd2c..360ee3a3a3ac7dac8b7130a27deb6aa7ba5c5bfc 100644 --- a/SimoxUtility/tests/filesystem/CMakeLists.txt +++ b/SimoxUtility/tests/filesystem/CMakeLists.txt @@ -1,2 +1,3 @@ ADD_SU_TEST( list_directory_test ) +ADD_SU_TEST( make_relative ) diff --git a/SimoxUtility/tests/filesystem/make_relative.cpp b/SimoxUtility/tests/filesystem/make_relative.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9185159721de55d9d3ba9d0702d04b942ab9ac36 --- /dev/null +++ b/SimoxUtility/tests/filesystem/make_relative.cpp @@ -0,0 +1,21 @@ +/** +* @package VirtualRobot +* @author Andre Meixner +* @copyright 2020 Andre Meixner +*/ + +#define BOOST_TEST_MODULE SimoxUtility/filesystem/make_relative + +#include <boost/test/included/unit_test.hpp> + +#include <SimoxUtility/filesystem/make_relative.h> + +namespace fs = simox::fs; + +BOOST_AUTO_TEST_CASE(make_relative) +{ + BOOST_CHECK_EQUAL(simox::fs::make_relative("a/b/c/e/", "a/b/d/f.xml"), "../../../d/f.xml"); + auto path = std::filesystem::path("a/b/c/e/g.xml").parent_path(); + BOOST_CHECK_EQUAL(simox::fs::make_relative(path, "a/b/d/f.xml"), "../../../d/f.xml"); + BOOST_CHECK_EQUAL(simox::fs::make_relative("a/b", "a/d/f.xml"), "../d/f.xml"); +} diff --git a/SimoxUtility/xml.h b/SimoxUtility/xml.h new file mode 100644 index 0000000000000000000000000000000000000000..5f454672c4f8b9ed79e909a3eadf444841604606 --- /dev/null +++ b/SimoxUtility/xml.h @@ -0,0 +1,5 @@ +#pragma once + +// This file is generated! + +#include "xml/rapidxml.h" diff --git a/SimoxUtility/xml/rapidxml.h b/SimoxUtility/xml/rapidxml.h new file mode 100644 index 0000000000000000000000000000000000000000..0f5e665bd3bab3091f56bad79ad9c01219956080 --- /dev/null +++ b/SimoxUtility/xml/rapidxml.h @@ -0,0 +1,6 @@ +#pragma once + +// This file is generated! + +#include "rapidxml/RapidXMLForwardDecl.h" +#include "rapidxml/RapidXMLWrapper.h" diff --git a/SimoxUtility/xml/rapidxml/RapidXMLForwardDecl.h b/SimoxUtility/xml/rapidxml/RapidXMLForwardDecl.h new file mode 100644 index 0000000000000000000000000000000000000000..a432705b43add05cffddc01a2f7a873a9f11ee1c --- /dev/null +++ b/SimoxUtility/xml/rapidxml/RapidXMLForwardDecl.h @@ -0,0 +1,49 @@ +#pragma once + +#include <memory> +#include <filesystem> +#include "SimoxUtility/error/SimoxError.h" + +namespace rapidxml +{ + template<class Ch> + class xml_node; +} + +namespace simox +{ + +namespace error +{ + class XMLFormatError : public SimoxError + { + public: + XMLFormatError(const std::string &message = std::string()) : SimoxError(message) {} + }; +} + +namespace xml +{ + +namespace attribute +{ + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + struct XMLAttribute + { + XMLAttribute(const std::string &attributeName) : attributeName(attributeName) + { + } + + std::string attributeName; + }; +} + +class RapidXMLWrapperNode; +typedef std::shared_ptr<RapidXMLWrapperNode> RapidXMLWrapperNodePtr; + +class RapidXMLWrapperRootNode; +typedef std::shared_ptr<RapidXMLWrapperRootNode> RapidXMLWrapperRootNodePtr; + +} + +} diff --git a/SimoxUtility/xml/rapidxml/RapidXMLWrapper.h b/SimoxUtility/xml/rapidxml/RapidXMLWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..7f37047f967dced8956b5b4a654ce60f46c6fc81 --- /dev/null +++ b/SimoxUtility/xml/rapidxml/RapidXMLWrapper.h @@ -0,0 +1,565 @@ +#ifndef __RapidXMLWrapper_H_ +#define __RapidXMLWrapper_H_ + +#include "rapidxml.hpp" +#include "rapidxml_print.hpp" + +#include "RapidXMLForwardDecl.h" + +#include <utility> +#include <iostream> +#include <fstream> +#include <memory> + +#include "SimoxUtility/algorithm/string.h" + +namespace simox { + +namespace xml { + +//! @brief Represents an xml node in the RapidXMLWrapper +class RapidXMLWrapperNode : public std::enable_shared_from_this<RapidXMLWrapperNode> +{ + + friend class RapidXMLWrapperRootNode; + +protected: + std::shared_ptr<rapidxml::xml_document<> > doc; + rapidxml::xml_node<>* node; + RapidXMLWrapperNodePtr parent; + + const char* cloneString(const std::string& str) { + return doc->allocate_string(str.c_str()); + } + + RapidXMLWrapperNode(std::shared_ptr<rapidxml::xml_document<> > doc, rapidxml::xml_node<>* node, RapidXMLWrapperNodePtr parent) + : doc(doc), node(node), parent(parent) + { } + + RapidXMLWrapperNode(std::shared_ptr<rapidxml::xml_document<> > doc, rapidxml::node_type node_type, RapidXMLWrapperNodePtr parent) : doc(doc), parent(parent) { + node = doc->allocate_node(node_type); + } + + RapidXMLWrapperNode(std::shared_ptr<rapidxml::xml_document<> > doc, rapidxml::node_type node_type, RapidXMLWrapperNodePtr parent, const std::string& name) : doc(doc), parent(parent) { + node = doc->allocate_node(node_type, cloneString(name)); + } + + void check() const { + if (!node) { + throw error::XMLFormatError("NullPointerException"); + } + } + + [[noreturn]] inline void throwError(const char* message) { + throw error::XMLFormatError("Error at " + getPath() + "! " + message); + } + + [[noreturn]] inline void rethrowXMLFormatError() { + try + { + throw; + } + catch (error::SimoxError &e) { + throwError(e.what()); + } + catch (...) { + throwError("Unknown Error."); + } + } + +public: + static RapidXMLWrapperNodePtr NullNode() { + RapidXMLWrapperNodePtr wrapper(new RapidXMLWrapperNode(std::shared_ptr<rapidxml::xml_document<> >(), NULL, nullptr)); + return wrapper; + } + + /** + * @brief get_node_ptr only for legacy code. + * @return internal pointer + */ + rapidxml::xml_node<>* get_node_ptr() const { + return node; + } + + /*! Returns the first child node (optional: with the given name). + @throws error::XMLFormatError if the node does not exist. */ + RapidXMLWrapperNodePtr first_node(const char* name = 0) { + check(); + + rapidxml::xml_node<>* node = this->node->first_node(name, 0, false); + + if (!node) throw error::XMLFormatError(std::string("Node '") + name + "' does not exist in node " + getPath()); + + RapidXMLWrapperNodePtr wrapper(new RapidXMLWrapperNode(doc, node, shared_from_this())); + return wrapper; + } + + /*! Returns the first child node with the given name and attribute. */ + RapidXMLWrapperNodePtr first_node_with_attribute(const char* name, const std::string &attrName, const std::string &attrValue) { + check(); + std::vector<RapidXMLWrapperNodePtr> n = nodes(name); + for (std::vector<RapidXMLWrapperNodePtr>::iterator it; n.begin(), it != n.end(); ++it) { + if ((*it)->has_attribute(attrName.c_str()) && (*it)->attribute_value(attrName.c_str()) == attrValue) { + return (*it); + } + } + return NullNode(); + } + + /*! Returns all child nodes (optional: just with the given name). */ + std::vector<RapidXMLWrapperNodePtr> nodes(const char* name = 0) { + std::vector<RapidXMLWrapperNodePtr> vec; + nodes(name, vec); + return vec; + } + + void nodes(const char* name, std::vector<RapidXMLWrapperNodePtr>& vec) { + for (RapidXMLWrapperNodePtr n = first_node(name); n->is_valid(); n = n->next_sibling(name)) { + vec.push_back(n); + } + } + + /*! Returns the attribute value. + @param attrName The name of the attribute. + @throws error::XMLFormatError if the attribute does not exist. */ + std::string attribute_value(const char* attrName) const { + check(); + rapidxml::xml_attribute<>* attrib = node->first_attribute(attrName, 0, false); + + if (!attrib) + { + throw error::XMLFormatError(std::string("Attribute '") + attrName + "' does not exist in node " + getPath()); + } + + return std::string(attrib->value()); + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + T attribute_value_(const char* attrName) { + try { return alg::to_<T>(attribute_value(attrName)); } catch (...) { rethrowXMLFormatError(); }; + } + + template<typename T> + T attribute_value_(const attribute::XMLAttribute<T> &attribute) { + try { return alg::to_<T>(attribute_value(attribute.attributeName.c_str())); } catch (...) { rethrowXMLFormatError(); }; + } + + std::vector<std::string> attribute_value_vec_str(const char* attrName, const std::string &delimiter = ";") { + try { return alg::split(attribute_value(attrName), delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + std::vector<std::string> attribute_value_vec_str(const char* attrName, unsigned int size, const std::string &delimiter = ";") { + try { return alg::split_check_size(attribute_value(attrName), size, delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + /*! Checks if a node has a specific attribute. + @param attrName The name of the attribute.*/ + bool has_attribute(const char* attrName) const + { + check(); + return node->first_attribute(attrName, 0, false) != 0; + } + + /*! Checks if a node has a specific child node. + @param attrName The name of the child node.*/ + bool has_node(const char* nodeName) const + { + check(); + return node->first_node(nodeName, 0, false) != 0; + } + + /*! Returns the value of an attribute if the attribute exists, otherwise a default value. + @param attrName The name of the attribute. + @param defaultValue The default value.*/ + std::string attribute_value_or_default(const char* attrName, const std::string& defaultValue) const + { + check(); + rapidxml::xml_attribute<>* attrib = node->first_attribute(attrName, 0, false); + + if (!attrib) + { + return defaultValue; + } + + return std::string(attrib->value()); + } + + std::vector<std::pair<std::string, std::string> > get_all_attributes() + { + check(); + std::vector<std::pair<std::string, std::string> > attributes; + + rapidxml::xml_attribute<>* attrib = node->first_attribute(0, 0, false); + + while (attrib) + { + std::string name = std::string(attrib->name()); + std::string value = std::string(attrib->value()); + std::pair<std::string, std::string> attrPair(name, value); + attributes.push_back(attrPair); + attrib = attrib->next_attribute(); + } + + return attributes; + } + + /*! Returns the content of an xml node.*/ + std::string value() const + { + check(); + return std::string(node->value()); + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + T value_() { + try { return alg::to_<T>(value()); } catch (...) { rethrowXMLFormatError(); }; + } + + std::vector<std::string> value_vec_str(const std::string &delimiter = ";") { + try { return alg::split(value(), delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + std::vector<std::string> value_vec_str(unsigned int size, const std::string &delimiter = ";") { + try { return alg::split_check_size(value(), size, delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + std::vector<T> value_vec_(const std::string &delimiter = "\t ") { + try { return alg::to_vec<T>(value(), delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + std::vector<T> value_vec_(unsigned int size, const std::string &delimiter = "\t ") { + try { return alg::to_vec<T>(value(), size, delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + Eigen::VectorXf value_eigen_vec(const std::string &delimiter = "\t ") { + try { return alg::to_eigen_vec(value(), delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + Eigen::VectorXf value_eigen_vec(unsigned int size, const std::string &delimiter = "\t ") { + try { return alg::to_eigen_vec_check_rows<float>(value(), size, delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + Eigen::Vector3f value_eigen_vec3(const std::string &delimiter = "\t ") { + try { return alg::to_eigen_vec<float, 3>(value(), delimiter); } catch (...) { rethrowXMLFormatError(); }; + } + + Eigen::Matrix4f value_matrix4f() { + Eigen::Matrix4f matrix; + for (int i = 1; i <= 4; i++) { + std::string row = "row" + simox::alg::to_string(i); + simox::xml::RapidXMLWrapperNodePtr rowNode = first_node(row.c_str()); + for (int j = 1; j <= 4; j++) { + std::string column = "c" + simox::alg::to_string(j); + matrix(i - 1, j - 1) = rowNode->attribute_value_<float>(column.c_str()); + } + } + return matrix; + } + + /*! Returns the name of an xml node.*/ + std::string name() const + { + check(); + return std::string(node->name()); + } + + rapidxml::node_type type() + { + check(); + return node->type(); + } + + std::string first_node_value(const char* nodeName = 0) const + { + check(); + rapidxml::xml_node<>* childNode = node->first_node(nodeName, 0, false); + + if (!childNode) + { + throw error::XMLFormatError(std::string("Node '") + nodeName + "' does not exist in node " + getPath()); + } + + return std::string(childNode->value()); + } + + std::string first_node_value_or_default(const char* name, const std::string& defaultValue) const + { + check(); + rapidxml::xml_node<>* childNode = node->first_node(name, 0, false); + + if (!childNode) + { + return defaultValue; + } + + return std::string(childNode->value()); + } + RapidXMLWrapperNodePtr next_sibling(const char* name = 0) + { + check(); + RapidXMLWrapperNodePtr wrapper(new RapidXMLWrapperNode(doc, node->next_sibling(name, 0, false), shared_from_this())); + return wrapper; + } + + bool is_valid() const + { + return node != NULL; + } + + std::string getPath() const + { + check(); + std::string result = name(); + rapidxml::xml_node<>* p = node->parent(); + + while (p) + { + result = std::string(p->name()) + "/" + result; + p = p->parent(); + } + + return result; + } + + /*! Appends a new attribute to the current node. + @return The current Node.*/ + RapidXMLWrapperNodePtr append_attribute(const std::string& name, const std::string& value) { + node->append_attribute(doc->allocate_attribute(cloneString(name), cloneString(value))); + return shared_from_this(); + } + + template<typename T> + RapidXMLWrapperNodePtr append_attribute(const attribute::XMLAttribute<T>& attribute, T value) { + return append_attribute(attribute.attributeName, alg::to_string(value)); // will not throw error + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + RapidXMLWrapperNodePtr append_attribute(const std::string& name, T value) { + try { return append_attribute(name, alg::to_string(value)); } catch (...) { rethrowXMLFormatError(); }; + } + + RapidXMLWrapperNodePtr append_attribute(const std::string& name, const std::vector<std::string> value, const std::string &delimiter = ";") { + try { return append_attribute(name, alg::to_string(value, delimiter)); } catch (...) { rethrowXMLFormatError(); }; + } + + /*! Appends a new node on the current node. + @return The new Node.*/ + RapidXMLWrapperNodePtr append_node(const std::string& name) { + RapidXMLWrapperNodePtr node(new RapidXMLWrapperNode(doc, rapidxml::node_element, shared_from_this(), name)); + this->node->append_node(node->node); + return node; + } + + /*! Appends a new node on the current node and deletes the first node with the same name + @return The new Node.*/ + RapidXMLWrapperNodePtr append_node_non_duplicate(const std::string& name) { + remove_first_node_if_present(name); + return append_node(name); + } + + /*! Adds std::string content to the current node. + @return The current Node.*/ + RapidXMLWrapperNodePtr append_data_node(const std::string& value) { + this->node->append_node(doc->allocate_node(rapidxml::node_data, 0, cloneString(value))); + return shared_from_this(); + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + RapidXMLWrapperNodePtr append_data_node(T value) { + try { return append_data_node(alg::to_string(value)); } catch (...) { rethrowXMLFormatError(); }; + } + + template<typename T, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> + RapidXMLWrapperNodePtr append_data_node(const std::vector<T> &value, const std::string &delimiter = " ") { + try { return append_data_node(alg::to_string(value, delimiter)); } catch (...) { rethrowXMLFormatError(); }; + } + + RapidXMLWrapperNodePtr append_data_node(const Eigen::VectorXf value, const std::string &delimiter = " ") { + try { return append_data_node(alg::to_string(value, delimiter)); } catch (...) { rethrowXMLFormatError(); }; + } + + RapidXMLWrapperNodePtr append_data_node(const std::vector<std::string> value, const std::string &delimiter = ";") { + try { return append_data_node(alg::to_string(value, delimiter)); } catch (...) { rethrowXMLFormatError(); }; + } + + RapidXMLWrapperNodePtr append_data_node(const Eigen::Matrix4f &matrix, const std::string &name) { + simox::xml::RapidXMLWrapperNodePtr matrixNode = append_node(name); + for (int i = 1; i <= 4; i++) { + std::string row = "row" + alg::to_string(i); + simox::xml::RapidXMLWrapperNodePtr rowNode = matrixNode->append_node(row); + for (int j = 1; j <= 4; j++) { + std::string column = "c" + alg::to_string(j); + rowNode->append_attribute(column, matrix(i - 1, j - 1)); + } + } + return matrixNode; + } + + RapidXMLWrapperNodePtr append_string_node(const std::string& name, const std::string& value) { + this->node->append_node(doc->allocate_node(rapidxml::node_element, cloneString(name), cloneString(value))); + return shared_from_this(); + } + + void remove_first_node(const std::string &name) { + RapidXMLWrapperNodePtr node = first_node(name.c_str()); + this->node->remove_node(node->node); + } + + void remove_first_node_if_present(const std::string &name) { + if (has_node(name.c_str())) remove_first_node(name); + } + + void remove_attribute(const std::string &name) { + this->node->remove_attribute(this->node->first_attribute(name.c_str())); + } + + void remove_attribute_if_present(const std::string &name) { + if (has_attribute(name.c_str())) remove_attribute(name); + } + + /*! Creates an xml std::string representation of this xml nodes' structure + @param indent Usage of tabs in the std::string representation for better readability. */ + std::string print(bool indent = true) { + std::string s; + rapidxml::print(std::back_inserter(s), *node, indent ? 0 : rapidxml::print_no_indenting); + return s; + } + + /*! Saves this xml nodes' structure as xml document. + @param path Path where the xml document should be saved. + @param indent Usage of tabs in the std::string representation for better readability.*/ + virtual void saveToFile(const std::string& path, bool indent = true) { + std::ofstream file; + file.open(path.c_str()); + file << print(indent); + file.close(); + } + + const rapidxml::xml_node<>* getNode() { + return node; + } +}; + +//! @brief Helper class for reading information from an xml format via Rapid XML +class RapidXMLWrapperRootNode : public RapidXMLWrapperNode +{ +private: + char* cstr; // The string must persist for the lifetime of the document. + std::filesystem::path path; + + RapidXMLWrapperRootNode(const std::string& xml, const std::filesystem::path &path = std::filesystem::path()) + : RapidXMLWrapperNode(getDocument(xml), NULL, nullptr), path(path) + { + node = doc->first_node(); + } + + RapidXMLWrapperRootNode(std::shared_ptr<rapidxml::xml_document<> > doc, const std::string &name) + : RapidXMLWrapperNode(doc, rapidxml::node_element, nullptr, name), path(std::filesystem::path()) + { + cstr = new char[0]; + } + + std::shared_ptr<rapidxml::xml_document<> > getDocument(const std::string& xml) { + if (xml.empty()) throw error::XMLFormatError(std::string("Empty xml!")); + cstr = new char[xml.size() + 1]; // Create char buffer to store std::string copy + strcpy(cstr, xml.c_str()); // Copy std::string into char buffer + try { + std::shared_ptr<rapidxml::xml_document<> > doc(new rapidxml::xml_document<>()); + doc->parse<0>(cstr); // Pass the non-const char* to parse() + return doc; + } + catch (rapidxml::parse_error e) { + std::string msg = e.what(); + throw error::XMLFormatError(std::string("No valid xml format! " + msg)); + } + } + +public: + ~RapidXMLWrapperRootNode() { + delete[] cstr; // free buffer memory when all is finished + } + +public: + static std::string ReadFileContents(const std::string& path) { + try { + std::ifstream in(path.c_str()); + + if (!in || !in.is_open()) { + throw error::XMLFormatError("Could not open XML file " + path); + } + + std::stringstream buffer; + buffer << in.rdbuf(); + std::string xmlString(buffer.str()); + in.close(); + + return xmlString; + } + catch (const std::ifstream::failure& e) { + throw error::XMLFormatError("Could not open XML file because " + std::string(e.what())); + } + } + + /*! + Creates a RapidXMLWrapper. + @param xml The xml as string. + @return The RapidXMLWrapper for the xml string. + */ + static RapidXMLWrapperRootNodePtr FromXmlString(const std::string& xml, const std::string &path = std::string()) { + RapidXMLWrapperRootNodePtr wrapper(new RapidXMLWrapperRootNode(xml, path)); + return wrapper; + } + + /*! + Creates a RapidXMLWrapper. + @param xml Path to an xml document. + @return The RapidXMLWrapper for the xml document. + */ + static RapidXMLWrapperRootNodePtr FromFile(const std::string& path) { + return FromXmlString(ReadFileContents(path), path); + } + + /*! Creates a root node with the given name. + @return The root Node. */ + static RapidXMLWrapperRootNodePtr createRootNode(const std::string& name) { + std::shared_ptr<rapidxml::xml_document<> > doc(new rapidxml::xml_document<>()); + RapidXMLWrapperNodePtr declaration(new RapidXMLWrapperNode(doc, rapidxml::node_declaration, nullptr)); + declaration->append_attribute("version", "1.0"); + declaration->append_attribute("encoding", "utf-8"); + doc->append_node(declaration->node); + RapidXMLWrapperRootNodePtr rootNode = RapidXMLWrapperRootNodePtr(new RapidXMLWrapperRootNode(doc, name)); + doc->append_node(rootNode->node); + return rootNode; + } + + /*! Saves the created xml structure as xml document. + @param path Path where the xml document should be saved. + @param indent Usage of tabs in the std::string representation for better readability.*/ + void saveToFile(const std::string& path, bool indent = true) override { + setPath(path); + RapidXMLWrapperNode::saveToFile(path, indent); + } + + /*! Saves the created xml structure as xml document at the same path it was loaded + @param indent Usage of tabs in the std::string representation for better readability. + @throws error::XMLFormatError if path value is empty */ + void saveToFile(bool indent = true) { + if (!path.empty()) saveToFile(path, indent); + else throw error::XMLFormatError("Empty path variable!"); + } + + void setPath(const std::string &path) { + this->path = path; + } + + std::string getPath() { + return path; + } +}; + +} +} + +#endif diff --git a/SimoxUtility/xml/rapidxml/rapidxml.hpp b/SimoxUtility/xml/rapidxml/rapidxml.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5bc4c0ba537caf44ba65d88e77621425490f4672 --- /dev/null +++ b/SimoxUtility/xml/rapidxml/rapidxml.hpp @@ -0,0 +1,2966 @@ +// #pragma once is deliberately not used to make it possible to include both ArmarX and Simox rapidxml +#ifndef __RAPIDXML_H_INCLUDE__ +#define __RAPIDXML_H_INCLUDE__ + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) +#include <cstdlib> // For std::size_t +#include <cassert> // For assert +#include <new> // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //! <br><br> + //! This function cannot return. If it does, the results are undefined. + //! <br><br> + //! A very simple definition might look like that: + //! <pre> + //! void %rapidxml::%parse_error_handler(const char *what, void *where) + //! { + //! std::cout << "Parse error: " << what << "\n"; + //! std::abort(); + //! } + //! </pre> + //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char* what, void* where); +} + +#else + +#include <exception> // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //! <br><br> + //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //! <br><br> + //! This class derives from <code>std::exception</code> class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char* what, void* where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + const char* what() const noexcept override + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template<class Ch> + Ch* where() const + { + return reinterpret_cast<Ch*>(m_where); + } + + private: + + const char* m_what; + void* m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE +// Size of static memory block of memory_pool. +// Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. +// No dynamic memory allocations are performed by memory_pool until static memory is exhausted. +#define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE +// Size of dynamic memory block of memory_pool. +// Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. +// After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. +#define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT +// Memory allocation alignment. +// Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. +// All memory allocations for nodes, attributes and strings will be aligned to this value. +// This must be a power of 2 and at least 1, otherwise memory_pool will not work. +#define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template<class Ch> class xml_node; + template<class Ch> class xml_attribute; + template<class Ch> class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //! <br><br> + //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //! <br><br> + //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a <i>negation</i> of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default, + //! and using the flag will disable it. + //! <br><br> + //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //! <ul> + //! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li> + //! <li>entities will not be translated</li> + //! <li>whitespace will not be normalized</li> + //! </ul> + //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //! <br><br> + //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //! <br><br> + //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template<int Dummy> + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template<class Ch> + inline std::size_t measure(const Ch* p) + { + const Ch* tmp = p; + + while (*tmp) + { + ++tmp; + } + + return tmp - p; + } + + // Compare strings for equality + template<class Ch> + inline bool compare(const Ch* p1, std::size_t size1, const Ch* p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + { + return false; + } + + if (case_sensitive) + { + for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + { + return false; + } + } + else + { + for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p2)]) + { + return false; + } + } + + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using <code>new</code> operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //! <br><br> + //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //! <br><br> + //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //! <br><br> + //! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each, + //! by using global <code>new[]</code> and <code>delete[]</code> operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //! <br><br> + //! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes. + //! This value defaults to the size of pointer on target architecture. + //! <br><br> + //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code> + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template<class Ch = char> + class memory_pool + { + + public: + + //! \cond internal + typedef void* (alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void*); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(nullptr) + , m_free_func(nullptr) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node<Ch>* allocate_node(node_type type, + const Ch* name = nullptr, const Ch* value = nullptr, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void* memory = allocate_aligned(sizeof(xml_node<Ch>)); + xml_node<Ch>* node = new(memory) xml_node<Ch>(type); + + if (name) + { + if (name_size > 0) + { + node->name(name, name_size); + } + else + { + node->name(name); + } + } + + if (value) + { + if (value_size > 0) + { + node->value(value, value_size); + } + else + { + node->value(value); + } + } + + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute<Ch>* allocate_attribute(const Ch* name = nullptr, const Ch* value = nullptr, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void* memory = allocate_aligned(sizeof(xml_attribute<Ch>)); + xml_attribute<Ch>* attribute = new(memory) xml_attribute<Ch>; + + if (name) + { + if (name_size > 0) + { + attribute->name(name, name_size); + } + else + { + attribute->name(name); + } + } + + if (value) + { + if (value_size > 0) + { + attribute->value(value, value_size); + } + else + { + attribute->value(value); + } + } + + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch* allocate_string(const Ch* source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + + if (size == 0) + { + size = internal::measure(source) + 1; + } + + Ch* result = static_cast<Ch*>(allocate_aligned(size * sizeof(Ch))); + + if (source) + for (std::size_t i = 0; i < size; ++i) + { + result[i] = source[i]; + } + + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node<Ch>* clone_node(const xml_node<Ch>* source, xml_node<Ch>* result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + { + result = allocate_node(source->type()); + } + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node<Ch>* child = source->first_node(); child; child = child->next_sibling()) + { + result->append_node(clone_node(child)); + } + + for (xml_attribute<Ch>* attr = source->first_attribute(); attr; attr = attr->next_attribute()) + { + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + } + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char* previous_begin = reinterpret_cast<header*>(align(m_begin))->previous_begin; + + if (m_free_func) + { + m_free_func(m_begin); + } + else + { + delete[] m_begin; + } + + m_begin = previous_begin; + } + + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use <code>longjmp()</code> function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //! <br><br> + //! User defined allocation functions must have the following forms: + //! <br><code> + //! <br>void *allocate(std::size_t size); + //! <br>void free(void *pointer); + //! </code><br> + //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func* af, free_func* ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char* previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char* align(char* ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char* allocate_raw(std::size_t size) + { + // Allocate + void* memory; + + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + { + RAPIDXML_PARSE_ERROR("out of memory", 0); + } + +#endif + } + + return static_cast<char*>(memory); + } + + void* allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char* result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + + if (pool_size < size) + { + pool_size = size; + } + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char* raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char* pool = align(raw_memory); + header* new_header = reinterpret_cast<header*>(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char* m_begin; // Start of raw memory making up current pool + char* m_ptr; // First free byte in current pool + char* m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func* m_alloc_func; // Allocator function, or 0 if default is to be used + free_func* m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template<class Ch = char> + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(nullptr) + , m_value(nullptr) + , m_parent(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //! <br><br> + //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch* name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //! <br><br> + //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch* value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //! <br><br> + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //! <br><br> + //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch* name, std::size_t size) + { + m_name = const_cast<Ch*>(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch* name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //! <br><br> + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //! <br><br> + //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! <br><br> + //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch* value, std::size_t size) + { + m_value = const_cast<Ch*>(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch* value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node<Ch>* parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch* nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch* m_name; // Name of node, or 0 if no name + Ch* m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node<Ch>* m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template<class Ch = char> + class xml_attribute: public xml_base<Ch> + { + + friend class xml_node<Ch>; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document<Ch>* document() const + { + if (xml_node<Ch>* node = this->parent()) + { + while (node->parent()) + { + node = node->parent(); + } + + return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0; + } + else + { + return 0; + } + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute<Ch>* previous_attribute(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_attribute<Ch>* attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + { + return attribute; + } + + return 0; + } + else + { + return this->m_parent ? m_prev_attribute : 0; + } + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute<Ch>* next_attribute(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_attribute<Ch>* attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + { + return attribute; + } + + return nullptr; + } + else + { + return this->m_parent ? m_next_attribute : nullptr; + } + } + + private: + + xml_attribute<Ch>* m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute<Ch>* m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //! <br><br> + //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template<class Ch = char> + class xml_node: public xml_base<Ch> + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(nullptr) + , m_first_attribute(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document<Ch>* document() const + { + xml_node<Ch>* node = const_cast<xml_node<Ch> *>(this); + + while (node->parent()) + { + node = node->parent(); + } + + return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node<Ch>* first_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_node<Ch>* child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + { + return child; + } + + return nullptr; + } + else + { + return m_first_node; + } + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node<Ch>* last_node(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_node<Ch>* child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + { + return child; + } + + return 0; + } + else + { + return m_last_node; + } + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node<Ch>* previous_sibling(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_node<Ch>* sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + { + return sibling; + } + + return 0; + } + else + { + return m_prev_sibling; + } + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node<Ch>* next_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_node<Ch>* sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + { + return sibling; + } + + return nullptr; + } + else + { + return m_next_sibling; + } + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute<Ch>* first_attribute(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_attribute<Ch>* attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + { + return attribute; + } + + return nullptr; + } + else + { + return m_first_attribute; + } + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute<Ch>* last_attribute(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + { + name_size = internal::measure(name); + } + + for (xml_attribute<Ch>* attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + { + return attribute; + } + + return 0; + } + else + { + return m_first_attribute ? m_last_attribute : 0; + } + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node<Ch>* child) + { + assert(child && !child->parent() && child->type() != node_document); + + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node<Ch>* child) + { + assert(child && !child->parent() && child->type() != node_document); + + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = nullptr; + m_first_node = child; + } + + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = nullptr; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node<Ch>* where, xml_node<Ch>* child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + + if (where == m_first_node) + { + prepend_node(child); + } + else if (where == 0) + { + append_node(child); + } + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node<Ch>* child = m_first_node; + m_first_node = child->m_next_sibling; + + if (child->m_next_sibling) + { + child->m_next_sibling->m_prev_sibling = 0; + } + else + { + m_last_node = 0; + } + + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node<Ch>* child = m_last_node; + + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + { + m_first_node = 0; + } + + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node<Ch>* where) + { + assert(where && where->parent() == this); + assert(first_node()); + + if (where == m_first_node) + { + remove_first_node(); + } + else if (where == m_last_node) + { + remove_last_node(); + } + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node<Ch>* node = first_node(); node; node = node->m_next_sibling) + { + node->m_parent = nullptr; + } + + m_first_node = nullptr; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute<Ch>* attribute) + { + assert(attribute && !attribute->parent()); + + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute<Ch>* attribute) + { + assert(attribute && !attribute->parent()); + + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = nullptr; + m_first_attribute = attribute; + } + + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = nullptr; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute<Ch>* where, xml_attribute<Ch>* attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + + if (where == m_first_attribute) + { + prepend_attribute(attribute); + } + else if (where == 0) + { + append_attribute(attribute); + } + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute<Ch>* attribute = m_first_attribute; + + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + { + m_last_attribute = 0; + } + + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute<Ch>* attribute = m_last_attribute; + + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + { + m_first_attribute = 0; + } + + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute<Ch>* where) + { + assert(first_attribute() && where->parent() == this); + + if (where == m_first_attribute) + { + remove_first_attribute(); + } + else if (where == m_last_attribute) + { + remove_last_attribute(); + } + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute<Ch>* attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + { + attribute->m_parent = nullptr; + } + + m_first_attribute = nullptr; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node&); + void operator =(const xml_node&); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node<Ch>* m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node<Ch>* m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute<Ch>* m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute<Ch>* m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node<Ch>* m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node<Ch>* m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template<class Ch = char> + class xml_document: public xml_node<Ch>, public memory_pool<Ch> + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node<Ch>(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //! <br><br> + //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //! <br><br> + //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template<int Flags> + void parse(Ch* text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom<Flags>(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip<whitespace_pred, Flags>(text); + + if (*text == 0) + { + break; + } + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + + if (xml_node<Ch>* node = parse_node<Flags>(text)) + { + this->append_node(node); + } + } + else + { + RAPIDXML_PARSE_ERROR("expected <", text); + } + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool<Ch>::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast<unsigned char>(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast<unsigned char>(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast<unsigned char>(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast<unsigned char>(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast<unsigned char>(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast<unsigned char>(ch)]; + } + }; + + // Detect attribute value character + template<Ch Quote> + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + { + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast<unsigned char>(ch)]; + } + + if (Quote == Ch('\"')) + { + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast<unsigned char>(ch)]; + } + + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template<Ch Quote> + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + { + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast<unsigned char>(ch)]; + } + + if (Quote == Ch('\"')) + { + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast<unsigned char>(ch)]; + } + + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template<int Flags> + static void insert_coded_character(Ch*& text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast<unsigned char>(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast<unsigned char>(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast<unsigned char>(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast<unsigned char>(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast<unsigned char>(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template<class StopPred, int Flags> + static void skip(Ch*& text) + { + Ch* tmp = text; + + while (StopPred::test(*tmp)) + { + ++tmp; + } + + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template<class StopPred, class StopPredPure, int Flags> + static Ch* skip_and_expand_character_refs(Ch*& text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip<StopPred, Flags>(text); + return text; + } + + // Use simple skip until first modification is detected + skip<StopPredPure, Flags>(text); + + // Use translation skip + Ch* src = text; + Ch* dest = src; + + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)]; + + if (digit == 0xFF) + { + break; + } + + code = code * 16 + digit; + ++src; + } + + insert_coded_character<Flags>(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)]; + + if (digit == 0xFF) + { + break; + } + + code = code * 10 + digit; + ++src; + } + + insert_coded_character<Flags>(dest, code); // Put character in output + } + + if (*src == Ch(';')) + { + ++src; + } + else + { + RAPIDXML_PARSE_ERROR("expected ;", src); + } + + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); + ++dest; // Put single space in dest + ++src; // Skip first whitespace char + + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + { + ++src; + } + + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template<int Flags> + void parse_bom(Ch*& text) + { + // UTF-8? + if (static_cast<unsigned char>(text[0]) == 0xEF && + static_cast<unsigned char>(text[1]) == 0xBB && + static_cast<unsigned char>(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration (<?xml...) + template<int Flags> + xml_node<Ch>* parse_xml_declaration(Ch*& text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + text += 2; // Skip '?>' + return nullptr; + } + + // Create declaration + xml_node<Ch>* declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip<whitespace_pred, Flags>(text); + + // Parse declaration attributes + parse_node_attributes<Flags>(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + { + RAPIDXML_PARSE_ERROR("expected ?>", text); + } + + text += 2; + + return declaration; + } + + // Parse XML comment (<!--...) + template<int Flags> + xml_node<Ch>* parse_comment(Ch*& text) + { + // If parsing of comments is disabled + if (!(Flags & parse_comment_nodes)) + { + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + text += 3; // Skip '-->' + return nullptr; // Do not produce comment node + } + + // Remember value start + Ch* value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + // Create comment node + xml_node<Ch>* comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + { + *text = Ch('\0'); + } + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template<int Flags> + xml_node<Ch>* parse_doctype(Ch*& text) + { + // Remember value start + Ch* value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + + while (depth > 0) + { + switch (*text) + { + case Ch('['): + ++depth; + break; + + case Ch(']'): + --depth; + break; + + case 0: + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node<Ch>* doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + *text = Ch('\0'); + } + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return nullptr; + } + + } + + // Parse PI + template<int Flags> + xml_node<Ch>* parse_pi(Ch*& text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node<Ch>* pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch* name = text; + skip<node_name_pred, Flags>(text); + + if (text == name) + { + RAPIDXML_PARSE_ERROR("expected PI target", text); + } + + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip<whitespace_pred, Flags>(text); + + // Remember start of pi + Ch* value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + text += 2; // Skip '?>' + return nullptr; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template<int Flags> + Ch parse_and_append_data(xml_node<Ch>* node, Ch*& text, Ch* contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + { + text = contents_start; + } + + // Skip until end of data + Ch* value = text, *end; + + if (Flags & parse_normalize_whitespace) + { + end = skip_and_expand_character_refs<text_pred, text_pure_with_ws_pred, Flags>(text); + } + else + { + end = skip_and_expand_character_refs<text_pred, text_pure_no_ws_pred, Flags>(text); + } + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + { + --end; + } + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + { + --end; + } + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node<Ch>* data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + { + node->value(value, end - value); + } + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template<int Flags> + xml_node<Ch>* parse_cdata(Ch*& text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + text += 3; // Skip ]]> + return nullptr; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch* value = text; + + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + // Create new cdata node + xml_node<Ch>* cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + *text = Ch('\0'); + } + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template<int Flags> + xml_node<Ch>* parse_element(Ch*& text) + { + // Create element node + xml_node<Ch>* element = this->allocate_node(node_element); + + // Extract element name + Ch* name = text; + skip<node_name_pred, Flags>(text); + + if (text == name) + { + RAPIDXML_PARSE_ERROR("expected element name", text); + } + + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip<whitespace_pred, Flags>(text); + + // Parse attributes, if any + parse_node_attributes<Flags>(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents<Flags>(text, element); + } + else if (*text == Ch('/')) + { + ++text; + + if (*text != Ch('>')) + { + RAPIDXML_PARSE_ERROR("expected >", text); + } + + ++text; + } + else + { + RAPIDXML_PARSE_ERROR("expected >", text); + } + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + { + element->name()[element->name_size()] = Ch('\0'); + } + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template<int Flags> + xml_node<Ch>* parse_node(Ch*& text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element<Flags>(text); + + // <?... + case Ch('?'): + ++text; // Skip ? + + if ((text[0] == Ch('x') || text[0] == Ch('X')) && + (text[1] == Ch('m') || text[1] == Ch('M')) && + (text[2] == Ch('l') || text[2] == Ch('L')) && + whitespace_pred::test(text[3])) + { + // '<?xml ' - xml declaration + text += 4; // Skip 'xml ' + return parse_xml_declaration<Flags>(text); + } + else + { + // Parse PI + return parse_pi<Flags>(text); + } + + // <!... + case Ch('!'): + + // Parse proper subset of <! node + switch (text[1]) + { + + // <!- + case Ch('-'): + if (text[2] == Ch('-')) + { + // '<!--' - xml comment + text += 3; // Skip '!--' + return parse_comment<Flags>(text); + } + + break; + + // <![ + case Ch('['): + if (text[2] == Ch('C') && text[3] == Ch('D') && text[4] == Ch('A') && + text[5] == Ch('T') && text[6] == Ch('A') && text[7] == Ch('[')) + { + // '<![CDATA[' - cdata + text += 8; // Skip '![CDATA[' + return parse_cdata<Flags>(text); + } + + break; + + // <!D + case Ch('D'): + if (text[2] == Ch('O') && text[3] == Ch('C') && text[4] == Ch('T') && + text[5] == Ch('Y') && text[6] == Ch('P') && text[7] == Ch('E') && + whitespace_pred::test(text[8])) + { + // '<!DOCTYPE ' - doctype + text += 9; // skip '!DOCTYPE ' + return parse_doctype<Flags>(text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with <! + ++text; // Skip ! + + while (*text != Ch('>')) + { + if (*text == 0) + { + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + + ++text; + } + + ++text; // Skip '>' + return nullptr; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template<int Flags> + void parse_node_contents(Ch*& text, xml_node<Ch>* node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch* contents_start = text; // Store start of node contents before whitespace is skipped + skip<whitespace_pred, Flags>(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. +after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '</' + + if (Flags & parse_validate_closing_tags) + { + // Skip and validate closing tag name + Ch* closing_name = text; + skip<node_name_pred, Flags>(text); + + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + { + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + } + else + { + // No validation, just skip name + skip<node_name_pred, Flags>(text); + } + + // Skip remaining whitespace after node name + skip<whitespace_pred, Flags>(text); + + if (*text != Ch('>')) + { + RAPIDXML_PARSE_ERROR("expected >", text); + } + + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + + if (xml_node<Ch>* child = parse_node<Flags>(text)) + { + node->append_node(child); + } + } + + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data<Flags>(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template<int Flags> + void parse_node_attributes(Ch*& text, xml_node<Ch>* node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch* name = text; + ++text; // Skip first character of attribute name + skip<attribute_name_pred, Flags>(text); + + if (text == name) + { + RAPIDXML_PARSE_ERROR("expected attribute name", name); + } + + // Create new attribute + xml_attribute<Ch>* attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip<whitespace_pred, Flags>(text); + + // Skip = + if (*text != Ch('=')) + { + RAPIDXML_PARSE_ERROR("expected =", text); + } + + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + { + attribute->name()[attribute->name_size()] = 0; + } + + // Skip whitespace after = + skip<whitespace_pred, Flags>(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + + if (quote != Ch('\'') && quote != Ch('"')) + { + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + } + + ++text; + + // Extract attribute value and expand char refs in it + Ch* value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + + if (quote == Ch('\'')) + { + end = skip_and_expand_character_refs < attribute_value_pred < Ch('\'') >, attribute_value_pure_pred < Ch('\'') >, AttFlags > (text); + } + else + { + end = skip_and_expand_character_refs < attribute_value_pred < Ch('"') >, attribute_value_pure_pred < Ch('"') >, AttFlags > (text); + } + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + { + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + } + + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + { + attribute->value()[attribute->value_size()] = 0; + } + + // Skip whitespace after attribute value + skip<whitespace_pred, Flags>(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, // 3 + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 4 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 5 + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 6 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 7 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 8 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 9 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // A + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // B + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // C + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // D + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // E + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 // F + }; + + // Upper case conversion + template<int Dummy> + const unsigned char lookup_tables<Dummy>::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, // 7 + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 8 + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 9 + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // A + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // B + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // C + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // D + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // E + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /*__RAPIDXML_H_INCLUDE__*/ diff --git a/SimoxUtility/xml/rapidxml/rapidxml_license.txt b/SimoxUtility/xml/rapidxml/rapidxml_license.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ab806d0af7905bec5932fe0d0a6b253f5951204 --- /dev/null +++ b/SimoxUtility/xml/rapidxml/rapidxml_license.txt @@ -0,0 +1,52 @@ +Use of this software is granted under one of the following two licenses, +to be chosen freely by the user. + +1. Boost Software License - Version 1.0 - August 17th, 2003 +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +2. The MIT License +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. \ No newline at end of file diff --git a/SimoxUtility/xml/rapidxml/rapidxml_print.hpp b/SimoxUtility/xml/rapidxml/rapidxml_print.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ceed57fac67dab7583d00c3bfe4b73f94423ccd4 --- /dev/null +++ b/SimoxUtility/xml/rapidxml/rapidxml_print.hpp @@ -0,0 +1,539 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED +#pragma once + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS +#include <ostream> +#include <iterator> +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template<class OutIt, class Ch> + inline OutIt copy_chars(const Ch* begin, const Ch* end, OutIt out) + { + while (begin != end) + { + *out++ = *begin++; + } + + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template<class OutIt, class Ch> + inline OutIt copy_and_expand_chars(const Ch* begin, const Ch* end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); + *out++ = Ch('l'); + *out++ = Ch('t'); + *out++ = Ch(';'); + break; + + case Ch('>'): + *out++ = Ch('&'); + *out++ = Ch('g'); + *out++ = Ch('t'); + *out++ = Ch(';'); + break; + + case Ch('\''): + *out++ = Ch('&'); + *out++ = Ch('a'); + *out++ = Ch('p'); + *out++ = Ch('o'); + *out++ = Ch('s'); + *out++ = Ch(';'); + break; + + case Ch('"'): + *out++ = Ch('&'); + *out++ = Ch('q'); + *out++ = Ch('u'); + *out++ = Ch('o'); + *out++ = Ch('t'); + *out++ = Ch(';'); + break; + + case Ch('&'): + *out++ = Ch('&'); + *out++ = Ch('a'); + *out++ = Ch('m'); + *out++ = Ch('p'); + *out++ = Ch(';'); + break; + + default: + *out++ = *begin; // No expansion, copy character + } + } + + ++begin; // Step to next character + } + + return out; + } + + // Fill given output iterator with repetitions of the same character + template<class OutIt, class Ch> + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + { + *out++ = ch; + } + + return out; + } + + // Find character + template<class Ch, Ch ch> + inline bool find_char(const Ch* begin, const Ch* end) + { + while (begin != end) + if (*begin++ == ch) + { + return true; + } + + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + /// Forward declarations + + template<class OutIt, class Ch> + OutIt print_children(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_element_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_attributes(OutIt out, const xml_node<Ch>* node, int flags); + + template<class OutIt, class Ch> + inline OutIt print_data_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_cdata_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_declaration_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_comment_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_doctype_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + template<class OutIt, class Ch> + inline OutIt print_pi_node(OutIt out, const xml_node<Ch>* node, int flags, int indent); + + // Print node + template<class OutIt, class Ch> + inline OutIt print_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + { + *out = Ch('\n'), ++out; + } + + // Return modified iterator + return out; + } + + // Print children of the node + template<class OutIt, class Ch> + inline OutIt print_children(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + for (xml_node<Ch>* child = node->first_node(); child; child = child->next_sibling()) + { + out = print_node(out, child, flags, indent); + } + + return out; + } + + // Print attributes of the node + template<class OutIt, class Ch> + inline OutIt print_attributes(OutIt out, const xml_node<Ch>* node, int flags) + { + for (xml_attribute<Ch>* attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + + // Print attribute value using appropriate quote type + if (find_char < Ch, Ch('"') > (attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + + return out; + } + + // Print data node + template<class OutIt, class Ch> + inline OutIt print_data_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_data); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template<class OutIt, class Ch> + inline OutIt print_cdata_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_cdata); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'); + ++out; + *out = Ch('!'); + ++out; + *out = Ch('['); + ++out; + *out = Ch('C'); + ++out; + *out = Ch('D'); + ++out; + *out = Ch('A'); + ++out; + *out = Ch('T'); + ++out; + *out = Ch('A'); + ++out; + *out = Ch('['); + ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); + ++out; + *out = Ch(']'); + ++out; + *out = Ch('>'); + ++out; + return out; + } + + // Print element node + template<class OutIt, class Ch> + inline OutIt print_element_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node<Ch>* child = node->first_node(); + + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == nullptr && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + { + *out = Ch('\n'), ++out; + } + + out = print_children(out, node, flags, indent + 1); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + + return out; + } + + // Print declaration node + template<class OutIt, class Ch> + inline OutIt print_declaration_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template<class OutIt, class Ch> + inline OutIt print_comment_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_comment); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template<class OutIt, class Ch> + inline OutIt print_doctype_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_doctype); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template<class OutIt, class Ch> + inline OutIt print_pi_node(OutIt out, const xml_node<Ch>* node, int flags, int indent) + { + assert(node->type() == node_pi); + + if (!(flags & print_no_indenting)) + { + out = fill_chars(out, indent, Ch('\t')); + } + + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template<class OutIt, class Ch> + inline OutIt print(OutIt out, const xml_node<Ch>& node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template<class Ch> + inline std::basic_ostream<Ch>& print(std::basic_ostream<Ch>& out, const xml_node<Ch>& node, int flags = 0) + { + print(std::ostream_iterator<Ch>(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template<class Ch> + inline std::basic_ostream<Ch>& operator <<(std::basic_ostream<Ch>& out, const xml_node<Ch>& node) + { + return print(out, node); + } + +#endif + +} +#endif diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt index 84e33388b047a70b8376640a4f6326fdc2c50a13..8e32dec13dc8bb6efd2e7dc038b346604599853c 100644 --- a/VirtualRobot/CMakeLists.txt +++ b/VirtualRobot/CMakeLists.txt @@ -217,6 +217,7 @@ SET(SOURCES IK/PoseQualityExtendedManipulability.cpp IK/PoseQualityManipulability.cpp IK/PoseQualityMeasurement.cpp + IK/RobotPoseDifferentialIK.cpp IK/StackedIK.cpp IK/SupportPolygon.cpp IK/constraints/BalanceConstraint.cpp @@ -418,6 +419,7 @@ SET(INCLUDES IK/PoseQualityExtendedManipulability.h IK/PoseQualityManipulability.h IK/PoseQualityMeasurement.h + IK/RobotPoseDifferentialIK.h IK/StackedIK.h IK/SupportPolygon.h IK/constraints/BalanceConstraint.h diff --git a/VirtualRobot/IK/RobotPoseDifferentialIK.cpp b/VirtualRobot/IK/RobotPoseDifferentialIK.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62ea677fa060538fb70e60dec9a6c2bd384fa391 --- /dev/null +++ b/VirtualRobot/IK/RobotPoseDifferentialIK.cpp @@ -0,0 +1,554 @@ +#include "RobotPoseDifferentialIK.h" + +#include <Eigen/QR> +#include <Eigen/Geometry> +#include <boost/format.hpp> +#include <boost/bind.hpp> +#include <boost/math/special_functions/fpclassify.hpp> +#include <VirtualRobot/VirtualRobotCommon.h> + +#include <algorithm> +#include <cfloat> + +using namespace std; + +namespace VirtualRobot { + + RobotPoseDifferentialIK::RobotPoseDifferentialIK(RobotPtr robot, RobotNodeSetPtr _rns, RobotNodePtr _coordSystem, JacobiProvider::InverseJacobiMethod invJacMethod) : + DifferentialIK(_rns, _coordSystem, invJacMethod) + { + this->robot = robot; + checkImprovement = true; + considerBoxConstraints = true; + // saving joint limits + _uLimits.resize(rns->getSize()); + _lLimits.resize(rns->getSize()); + for (unsigned int i=0; i<rns->getSize(); i++) { + RobotNodePtr n = rns->getNode(i); + _lLimits[i] = n->getJointLimitLow(); + _uLimits[i] = n->getJointLimitHigh(); + } + } + + + Eigen::MatrixXf RobotPoseDifferentialIK::getJacobianMatrix() + { + if (nRows == 0) + this->setNRows(); + size_t nDoF = nodes.size(); + + nDoF += 6; // add 6 DoF for robot pose + + Eigen::MatrixXf Jacobian = Eigen::MatrixXf::Constant(nRows, nDoF,0.0f); + + size_t index = 0; + for (size_t i = 0; i < tcp_set.size(); i++) + { + SceneObjectPtr tcp = tcp_set[i]; + if (this->targets.find(tcp) != this->targets.end()) + { + IKSolver::CartesianSelection mode = this->modes[tcp]; + Eigen::MatrixXf partJacobian = this->getJacobianMatrix(tcp, mode); + this->localJacobians[i] = partJacobian; + + Jacobian.block(index, 0, partJacobian.rows(), nDoF) = partJacobian.block(0, 0, partJacobian.rows(), nDoF); + if (mode & IKSolver::X) + index++; + if (mode & IKSolver::Y) + index++; + if (mode & IKSolver::Z) + index++; + if (mode & IKSolver::Orientation) + index += 3; + } + else + VR_ERROR << "Internal error?!" << endl; // Error + } + return Jacobian; + } + + + Eigen::MatrixXf RobotPoseDifferentialIK::getJacobianMatrix(SceneObjectPtr tcp, IKSolver::CartesianSelection mode) + { + // Get number of degrees of freedom + size_t nDoF = nodes.size(); + + // using robot position and rotation as dof + nDoF += 6; + + // obtain the size of the matrix. + unsigned int size = 0; + if (mode & IKSolver::X) size++; + if (mode & IKSolver::Y) size++; + if (mode & IKSolver::Z) size++; + if (mode & IKSolver::Orientation) size += 3; + + Eigen::MatrixXf jacJoints = DifferentialIK::getJacobianMatrix(tcp, mode); + Eigen::MatrixXf jacRobot(size, 6); + + Eigen::MatrixXf positionRobot = Eigen::MatrixXf::Zero(3, 6); + Eigen::MatrixXf orientationRobot = Eigen::MatrixXf::Zero(3, 6); + // Add DoF related to robot pose + for (size_t i = 0; i < 6; i++) + { + + // robot pose + if (i < 3) + { + // pos + + Eigen::Vector3f axis; + if (i == 0) + axis = Eigen::Vector3f::UnitX(); + else if (i == 1) + axis = Eigen::Vector3f::UnitY(); + else if (i == 2) + axis = Eigen::Vector3f::UnitZ(); + else + { + cout << "int. err. " << endl; + continue; + } + + if (coordSystem) + { + // convert Vector to local coord system + Eigen::Vector4f result4f = Eigen::Vector4f::Zero(); + result4f.segment(0, 3) = axis; + result4f = coordSystem->getGlobalPose().inverse() * result4f; + axis = result4f.head(3); + } + // if necessary calculate the position part of the Jacobian + if (mode & IKSolver::Position) + positionRobot.block(0, i, 3, 1) = axis; + // no orientation part required with prismatic joints + } + else + { + // ori + + Eigen::Vector3f axis; + if (i == 3) + axis = Eigen::Vector3f::UnitX(); + else if (i == 4) + axis = Eigen::Vector3f::UnitY(); + else if (i == 5) + axis = Eigen::Vector3f::UnitZ(); + else + { + cout << "int. err. " << endl; + continue; + } + if (coordSystem) + { + // convert Vector to local coord system + Eigen::Vector4f result4f = Eigen::Vector4f::Zero(); + result4f.segment(0, 3) = axis; + result4f = coordSystem->getGlobalPose().inverse() * result4f; + axis = result4f.head(3); + } + // if necessary calculate the position part of the Jacobian + if (mode & IKSolver::Position) + { + Eigen::Vector3f toTCP; + if (coordSystem) + { + toTCP = coordSystem->toLocalCoordinateSystem(tcp->getGlobalPose()).block(0, 3, 3, 1) + - coordSystem->toLocalCoordinateSystem(robot->getGlobalPose()).block(0, 3, 3, 1); + } + else + { + toTCP = tcp->getGlobalPose().block(0, 3, 3, 1) + - robot->getGlobalPose().block(0, 3, 3, 1); + } + if (convertMMtoM) + toTCP /= 1000.0f; + /*cout << "toTCP: " << tcp->getName() << endl; + cout << axis << endl; + cout << toTCP << endl;*/ + Eigen::Vector3f r = axis.cross(toTCP); + //cout << r << endl; + positionRobot.block(0, i, 3, 1) = r; + if (r.norm() > 1e10) + { + cout << "posRobot error" << endl; + } + } + // and the orientation part + if (mode & IKSolver::Orientation) + orientationRobot.block(0, i, 3, 1) = axis; + } + } + + Eigen::MatrixXf result(size, nDoF); + result.block(0, 0, size, nDoF - 6) = jacJoints; + + // copy only what is required (and was previously calculated) + unsigned int index = 0; + if (mode & IKSolver::X) + { + result.block(index, nDoF - 6, 1, 6) = positionRobot.row(0); + //result.block(0, nDoF - 6 + index, size, 1) = positionRobot.col(0); + index++; + } + if (mode & IKSolver::Y) + { + result.block(index, nDoF - 6, 1, 6) = positionRobot.row(1); + index++; + } + if (mode & IKSolver::Z) + { + result.block(index, nDoF - 6, 1, 6) = positionRobot.row(2); + index++; + } + if (mode & IKSolver::Orientation) + { + result.block(index, nDoF - 6, 3, 6) = orientationRobot; + } + return result; + } + + Eigen::MatrixXd RobotPoseDifferentialIK::computePseudoInverseJacobianMatrixDampedD(const Eigen::MatrixXd &m) + { + Eigen::JacobiSVD<Eigen::MatrixXd> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV); + Eigen::MatrixXd U = svd.matrixU(); + Eigen::MatrixXd V = svd.matrixV(); + Eigen::VectorXd sv = svd.singularValues(); + + //float lambda = 1.0f; + + double epsilon = std::numeric_limits<double>::epsilon(); + double tol = epsilon*std::max(m.rows(),m.cols())*m.norm(); + //tol = 0.001; + //MMM_INFO << "tol" << tol << endl; + for (int i = 0; i<sv.rows(); i++) + { + if (sv(i)>tol) + sv(i) = 1.0f/ sv(i) ; +// sv(i) = sv(i) / (sv(i)*sv(i) + lambda*lambda); + else + sv(i) = 0.0f; + } + + /*if (sv(i) > tol) + sv(i) = 1.0f / sv(i); + else sv(i) = 0;*/ + + return (V*sv.asDiagonal()*U.transpose()); + } + + + Eigen::VectorXf RobotPoseDifferentialIK::computeStep(float stepSize) + { + const bool considerMaxAngle = true; + + if (nRows == 0) this->setNRows(); + size_t nDoF = nodes.size(); + + // consider robot pose as 6d vector + nDoF += 6; + //this->inverseMethod = DifferentialIK::eSVDDamped; + bool jVerbose = verbose; + jVerbose = false; + + // init + bool bAbort = false; + Eigen::VectorXf thetaOld(nDoF); + Eigen::VectorXf theta(nDoF); + Eigen::VectorXf blockedJointDeltas(nDoF); + Eigen::VectorXf blockedJointValues(nDoF); + bool bViolation; + std::vector<int> blockedJoints; + Eigen::VectorXf dTheta(nDoF); + //Eigen::MatrixXf pseudo; + float maximal = float(10 * M_PI /180.0); + float scale; + Eigen::VectorXf error; + Eigen::VectorXf angularDiff(nDoF-3); + Eigen::MatrixXf Jacobian; + float maxAngularDifference;// , maxAngularDifference1, maxAngularDifference2; + + + + if (considerBoxConstraints) + { + + // get joint positions + rns->getJointValues(thetaOld); + blockedJointValues = thetaOld; + blockedJointDeltas.setZero(); + // main loop + int counter = 0; + while (!bAbort && counter < 30) { + counter++; + scale = 1.0; + error = getError(stepSize); + Jacobian = getJacobianMatrix(); + + int rows = Jacobian.rows(); + for (int blockedJoint : blockedJoints) { + Jacobian.block(0, blockedJoint, rows, 1).setZero(); + } + + + + /* + MatrixXd JacobianXd = Jacobian.cast<double>(); + MatrixXd test = Jacobian.transpose().cast<double>()*Jacobian.cast<double>(); + FullPivLU<MatrixXd> lu_decomp(test); + cout << "The rank of A is " << lu_decomp.rank() << " (" << test.rows() << "x" << test.cols() << ")" << endl; + */ + + Eigen::MatrixXf pseudoXf = computePseudoInverseJacobianMatrix(Jacobian); + Eigen::VectorXf dThetaXf = pseudoXf * error; + // Eigen::VectorXf errorXf = Jacobian * dThetaXf; + // MMM_INFO << error.norm() << ", " << errorXf.norm() << endl; + dTheta = dThetaXf; + + /* + MatrixXd pseudoXd = computePseudoInverseJacobianMatrixDampedD(JacobianXd); + VectorXd dThetaXd = pseudoXd * error.cast<double>(); + dTheta = dThetaXd.cast<float>(); + Eigen::VectorXd errorXd = JacobianXd * dThetaXd; + */ + + + /* Debug Stuff */ + // MMM_INFO << error.norm() << ", " << errorXd.norm() << endl; + //MMM_INFO << dThetaXd.transpose() << endl << dThetaXf.transpose() << endl; + + + //cout << "error:" << error << endl; + //cout << "THETA:" << dTheta << endl; + if (considerMaxAngle) + { + scale = 1.0f; + angularDiff.block(0, 0, nDoF - 6, 1) = dTheta.head(nDoF - 6); + angularDiff.block(nDoF - 6, 0, 3, 1) = dTheta.tail(3); + //cout << "angularDiff:" << angularDiff << endl; + maxAngularDifference = angularDiff.array().abs().maxCoeff(); + //cout << "maxAngularDifference:" << maxAngularDifference << endl; + //if (angularDiff.norm() > maximal) // why norm?! + if (maxAngularDifference > maximal ) + //scale = maximal / angularDiff.norm(); // * stepSize; + scale = float(maximal / maxAngularDifference) * stepSize; + dTheta *= scale; + } + + // calculate new joint positions after applying deltas + theta = thetaOld + dTheta.block(0, 0, nDoF - 6, 1); + // check for violation + bViolation = false; + for (unsigned int i = 0; i < nDoF - 6; i++) { + // lower limit violated? + if (theta[i] < _lLimits[i]) { + // save delta, and apply limit + blockedJointDeltas[i] = _lLimits[i] - thetaOld[i]; + theta[i] = _lLimits[i]; + blockedJointValues[i] = _lLimits[i]; + } + // upper limit violated? + else if (theta[i] > _uLimits[i]) { + // save delta, and apply limit + blockedJointDeltas[i] = _uLimits[i] - thetaOld[i]; + theta[i] = _uLimits[i]; + blockedJointValues[i] = _uLimits[i]; + } + else + // no violation? -> next + continue; + // one limit was violated, so mark this joint as fixed + bViolation = true; + blockedJoints.push_back(i); + // blocking one joint at a time + break; + } + // abort if no new joints were locked, rerun otherwise + if (!bViolation) { + bAbort = true; + } + /*else { + // todo: check if necessary + rns->setJointValues(blockedJointValues); + }*/ + + } + // restore original robot configuration + //rns->setJointValues(thetaOld); + // restore real delta theta + if (jVerbose) + if (blockedJoints.size() != 0) + std::cout << "These joints were blocked at their limits: "; + for (int index : blockedJoints) { + dTheta[index] = blockedJointDeltas[index]; + if (jVerbose) + std::cout << index << " " << rns->getNode(index)->getName() << "\t"; + } + if (jVerbose) + if (blockedJoints.size() != 0) + std::cout << std::endl; + //cout << "counter:" << counter << endl; + return dTheta; + } else + { + // no box constraints + error = getError(stepSize); + if (jVerbose) + cout << "Error Cartesian:" << error.transpose() << endl << endl; + Jacobian = getJacobianMatrix(); + Eigen::MatrixXf pseudoXf = computePseudoInverseJacobianMatrix(Jacobian); + if (jVerbose) + cout << "PseudoInv min/max:" << endl << pseudoXf.minCoeff() << "," << pseudoXf.maxCoeff() << endl << endl; + Eigen::VectorXf dThetaXf = pseudoXf * error; + dTheta = dThetaXf; + if (jVerbose) + cout << "dTheta:" << dTheta.transpose() << endl << endl; + + if (considerMaxAngle) + { + scale = 1.0f; + angularDiff.block(0, 0, nDoF - 6, 1) = dTheta.head(nDoF - 6); + angularDiff.block(nDoF - 6, 0, 3, 1) = dTheta.tail(3); + + //cout << "angularDiff:" << angularDiff << endl; + maxAngularDifference = angularDiff.array().abs().maxCoeff(); + //maxAngularDifference = angularDiff.norm(); + //cout << "maxAngularDifference:" << maxAngularDifference << endl; + if (maxAngularDifference > maximal ) + //if (angularDiff.norm() > maximal) + { + scale = maximal / maxAngularDifference; + if (jVerbose) + cout << "Cutting, maxAngularDifference=" << maxAngularDifference << ", scale = " << scale << endl << endl; + } + dTheta *= scale; + } + return dTheta; + } + } + + + bool RobotPoseDifferentialIK::checkTolerances() + { + for (auto tcp : tcp_set){ + if (getErrorPosition(tcp) > tolerancePosition[tcp] || getErrorRotation(tcp)>toleranceRotation[tcp]) + { + return false; + } + } + return true; + } + + void RobotPoseDifferentialIK::boxConstraints(bool enable) + { + considerBoxConstraints = enable; + } + + bool RobotPoseDifferentialIK::computeSteps(float stepSize, float minChange, int maxNStep, bool performMinOneStep) + { + VR_ASSERT(rns); + VR_ASSERT(nodes.size() == rns->getSize()); + VR_ASSERT(robot); + std::vector<float> jv(nodes.size(), 0.0f); + std::vector<float> jvBest = rns->getJointValues(); + Eigen::Matrix4f bestPose = robot->getGlobalPose(); + int step = 0; + checkTolerances(); + float lastDist = getMeanErrorPosition(); + float bestDist = lastDist; + int nrImprovements = 0; + this->localJacobians.resize(this->tcp_set.size()); + + //cout << verbose << endl; + while (step < maxNStep) + { + Eigen::VectorXf dTheta = this->computeStep(stepSize); + + for (unsigned int i = 0; i < nodes.size(); i++) + { + jv[i] = (nodes[i]->getJointValue() + dTheta[i]); + if (boost::math::isnan(jv[i]) || boost::math::isinf(jv[i])) + { + VR_WARNING << "Aborting, invalid joint value (nan)" << endl; + return false; + } + } + + // update pose + Eigen::Matrix3f m; + m = Eigen::AngleAxisf(dTheta[nodes.size() + 3], Eigen::Vector3f::UnitX()) + * Eigen::AngleAxisf(dTheta[nodes.size() + 4], Eigen::Vector3f::UnitY()) + * Eigen::AngleAxisf(dTheta[nodes.size() + 5], Eigen::Vector3f::UnitZ()); + Eigen::Vector3f pos(dTheta[nodes.size() + 0], dTheta[nodes.size() + 1], dTheta[nodes.size() + 2]); + Eigen::Matrix4f deltaPose = Eigen::Matrix4f::Identity(); + deltaPose.block(0, 0, 3, 3) = m; + deltaPose.block(0, 3, 3, 1) = pos; + + Eigen::Matrix4f resPose = deltaPose * robot->getGlobalPose(); + robot->setGlobalPose(resPose); + + if (considerBoxConstraints && !rns->checkJointLimits(jv)){ + VR_ERROR << "Joint limit violated" << endl; + } + robot->setJointValues(rns, jv); + + + // check tolerances + if (checkTolerances()) + { + if (verbose) + VR_INFO << "Tolerances ok, loop:" << step << endl; + return true; + } + + float posDist = getMeanErrorPosition(); + + // ensure that at least one step is performed (step==0 -> store best solution) + if ( (performMinOneStep && step == 0) || posDist<bestDist) + { + if (verbose && step != 0) + VR_INFO << "Loop:" << step << ", best IK dist: " << posDist << endl; + + bestPose = robot->getGlobalPose(); + jvBest = jv; + bestDist = posDist; + nrImprovements++; + } + if (checkImprovement && posDist>lastDist) + { + if (verbose) + VR_INFO << "Could not improve result any more (current position error=" << posDist << ", last loop's error:" << lastDist << "), loop:" << step << endl; + robot->setGlobalPose(bestPose); + robot->setJointValues(rns, jvBest); + return false; + } + float d = dTheta.norm(); + if (dTheta.norm()<minChange) + { + if (verbose) + VR_INFO << "Could not improve result any more (dTheta.norm()=" << d << "), loop:" << step << endl; + + // set best result + robot->setGlobalPose(bestPose); + robot->setJointValues(rns, jvBest); + return false; // local minimum + } + + lastDist = posDist; + step++; + } + + // set best result + robot->setGlobalPose(bestPose); + robot->setJointValues(rns, jvBest); + if (verbose && maxNStep > 1) + { + VR_INFO << "IK failed, improvementSteps:" << nrImprovements << ", loop:" << step << endl; + VR_INFO << "pos error:" << getMeanErrorPosition() << endl; + VR_INFO << "rot error (tcp 0):" << getErrorRotation(tcp_set[0]) << endl; + } + return false; + } + +} + diff --git a/VirtualRobot/IK/RobotPoseDifferentialIK.h b/VirtualRobot/IK/RobotPoseDifferentialIK.h new file mode 100644 index 0000000000000000000000000000000000000000..51b4c3d72d5c01bd669a9e825a53ac1a742e7daa --- /dev/null +++ b/VirtualRobot/IK/RobotPoseDifferentialIK.h @@ -0,0 +1,86 @@ +/** +* This file is part of Simox. +* +* Simox is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* Simox is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package VirtualRobot +* @author Nikolaus Vahrenkamp +* @copyright 2013 Nikolaus Vahrenkamp +* GNU Lesser General Public License +* +*/ + +#pragma once + +#include "DifferentialIK.h" + +#include <string> +#include <vector> + +namespace VirtualRobot +{ + class [[deprecated("MMMTools_LegacyConverter")]] VIRTUAL_ROBOT_IMPORT_EXPORT RobotPoseDifferentialIK : public DifferentialIK, public std::enable_shared_from_this<RobotPoseDifferentialIK> + { + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + /*! + @brief Initialize a Jacobian object. + \param rns The robotNodes (i.e., joints) for which the Jacobians should be calculated. + \param coordSystem The coordinate system in which the Jacobians are defined. By default the global coordinate system is used. + */ + RobotPoseDifferentialIK(RobotPtr robot,RobotNodeSetPtr rns, RobotNodePtr coordSystem = RobotNodePtr(), JacobiProvider::InverseJacobiMethod invJacMethod = eSVDDamped); + + Eigen::MatrixXf getJacobianMatrix(SceneObjectPtr tcp, IKSolver::CartesianSelection mode); + + Eigen::MatrixXf getJacobianMatrix(); + + /*! @brief Compute a single IK step. + @param stepSize Controls the amount of error to be reduced in each step: \f$ 0 < \beta \leq 1 \f$ + @return The changes \f$\Delta \theta\f$ in the joint angles. + \note{Note} This does not affect the joints values of the robot. + */ + Eigen::VectorXf computeStep(float stepSize=1.0); + + /*! @brief Computes the complete inverse kinematics. + @param stepSize Controls the amount of error to be reduced in each step: \f$ 0 < \beta \leq 1 \f$ + @param maxSteps Maximal numbers of steps. + @param minChange The minimal change in joint angles (euclidean distance in radians) + @param performMinOneStep If set, at least one step is performed (helps escaping local minima, but may also cause pose jittering) + @note{Note} Sets the node's joint angles automatically. + */ + bool computeSteps(float stepSize, float minChange, int maxSteps, bool performMinOneStep = true); + + /*! + If enabled (standard), joint limits are considered via box constraints. + */ + void boxConstraints(bool enable); + + protected: + + bool checkTolerances(); + + Eigen::MatrixXd computePseudoInverseJacobianMatrixDampedD(const Eigen::MatrixXd &m); + std::vector<Eigen::MatrixXf> localJacobians; + RobotPtr robot; + + bool considerBoxConstraints; + + Eigen::VectorXf _lLimits; + Eigen::VectorXf _uLimits; + }; + + [[deprecated("MMMTools_LegacyConverter")]] typedef std::shared_ptr<RobotPoseDifferentialIK> RobotPoseDifferentialIKPtr; +} + diff --git a/VirtualRobot/XML/rapidxml.hpp b/VirtualRobot/XML/rapidxml.hpp index 5bc4c0ba537caf44ba65d88e77621425490f4672..7d26fb08cb4d11557caf3818202995463aca3944 100644 --- a/VirtualRobot/XML/rapidxml.hpp +++ b/VirtualRobot/XML/rapidxml.hpp @@ -1,2966 +1 @@ -// #pragma once is deliberately not used to make it possible to include both ArmarX and Simox rapidxml -#ifndef __RAPIDXML_H_INCLUDE__ -#define __RAPIDXML_H_INCLUDE__ - -// Copyright (C) 2006, 2009 Marcin Kalicinski -// Version 1.13 -// Revision $DateTime: 2009/05/13 01:46:17 $ -//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation - -// If standard library is disabled, user must provide implementations of required functions and typedefs -#if !defined(RAPIDXML_NO_STDLIB) -#include <cstdlib> // For std::size_t -#include <cassert> // For assert -#include <new> // For placement new -#endif - -// On MSVC, disable "conditional expression is constant" warning (level 4). -// This warning is almost impossible to avoid with certain types of templated code -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4127) // Conditional expression is constant -#endif - -/////////////////////////////////////////////////////////////////////////// -// RAPIDXML_PARSE_ERROR - -#if defined(RAPIDXML_NO_EXCEPTIONS) - -#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } - -namespace rapidxml -{ - //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, - //! this function is called to notify user about the error. - //! It must be defined by the user. - //! <br><br> - //! This function cannot return. If it does, the results are undefined. - //! <br><br> - //! A very simple definition might look like that: - //! <pre> - //! void %rapidxml::%parse_error_handler(const char *what, void *where) - //! { - //! std::cout << "Parse error: " << what << "\n"; - //! std::abort(); - //! } - //! </pre> - //! \param what Human readable description of the error. - //! \param where Pointer to character data where error was detected. - void parse_error_handler(const char* what, void* where); -} - -#else - -#include <exception> // For std::exception - -#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) - -namespace rapidxml -{ - - //! Parse error exception. - //! This exception is thrown by the parser when an error occurs. - //! Use what() function to get human-readable error message. - //! Use where() function to get a pointer to position within source text where error was detected. - //! <br><br> - //! If throwing exceptions by the parser is undesirable, - //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. - //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. - //! This function must be defined by the user. - //! <br><br> - //! This class derives from <code>std::exception</code> class. - class parse_error: public std::exception - { - - public: - - //! Constructs parse error - parse_error(const char* what, void* where) - : m_what(what) - , m_where(where) - { - } - - //! Gets human readable description of error. - //! \return Pointer to null terminated description of the error. - const char* what() const noexcept override - { - return m_what; - } - - //! Gets pointer to character data where error happened. - //! Ch should be the same as char type of xml_document that produced the error. - //! \return Pointer to location within the parsed string where error occured. - template<class Ch> - Ch* where() const - { - return reinterpret_cast<Ch*>(m_where); - } - - private: - - const char* m_what; - void* m_where; - - }; -} - -#endif - -/////////////////////////////////////////////////////////////////////////// -// Pool sizes - -#ifndef RAPIDXML_STATIC_POOL_SIZE -// Size of static memory block of memory_pool. -// Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. -// No dynamic memory allocations are performed by memory_pool until static memory is exhausted. -#define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) -#endif - -#ifndef RAPIDXML_DYNAMIC_POOL_SIZE -// Size of dynamic memory block of memory_pool. -// Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. -// After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. -#define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) -#endif - -#ifndef RAPIDXML_ALIGNMENT -// Memory allocation alignment. -// Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. -// All memory allocations for nodes, attributes and strings will be aligned to this value. -// This must be a power of 2 and at least 1, otherwise memory_pool will not work. -#define RAPIDXML_ALIGNMENT sizeof(void *) -#endif - -namespace rapidxml -{ - // Forward declarations - template<class Ch> class xml_node; - template<class Ch> class xml_attribute; - template<class Ch> class xml_document; - - //! Enumeration listing all node types produced by the parser. - //! Use xml_node::type() function to query node type. - enum node_type - { - node_document, //!< A document node. Name and value are empty. - node_element, //!< An element node. Name contains element name. Value contains text of first data node. - node_data, //!< A data node. Name is empty. Value contains data text. - node_cdata, //!< A CDATA node. Name is empty. Value contains data text. - node_comment, //!< A comment node. Name is empty. Value contains comment text. - node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. - node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. - node_pi //!< A PI node. Name contains target. Value contains instructions. - }; - - /////////////////////////////////////////////////////////////////////// - // Parsing flags - - //! Parse flag instructing the parser to not create data nodes. - //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_no_data_nodes = 0x1; - - //! Parse flag instructing the parser to not use text of first data node as a value of parent element. - //! Can be combined with other flags by use of | operator. - //! Note that child data nodes of element node take precendence over its value when printing. - //! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored. - //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. - //! <br><br> - //! See xml_document::parse() function. - const int parse_no_element_values = 0x2; - - //! Parse flag instructing the parser to not place zero terminators after strings in the source text. - //! By default zero terminators are placed, modifying source text. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_no_string_terminators = 0x4; - - //! Parse flag instructing the parser to not translate entities in the source text. - //! By default entities are translated, modifying source text. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_no_entity_translation = 0x8; - - //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. - //! By default, UTF-8 handling is enabled. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_no_utf8 = 0x10; - - //! Parse flag instructing the parser to create XML declaration node. - //! By default, declaration node is not created. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_declaration_node = 0x20; - - //! Parse flag instructing the parser to create comments nodes. - //! By default, comment nodes are not created. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_comment_nodes = 0x40; - - //! Parse flag instructing the parser to create DOCTYPE node. - //! By default, doctype node is not created. - //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_doctype_node = 0x80; - - //! Parse flag instructing the parser to create PI nodes. - //! By default, PI nodes are not created. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_pi_nodes = 0x100; - - //! Parse flag instructing the parser to validate closing tag names. - //! If not set, name inside closing tag is irrelevant to the parser. - //! By default, closing tags are not validated. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_validate_closing_tags = 0x200; - - //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. - //! By default, whitespace is not trimmed. - //! This flag does not cause the parser to modify source text. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_trim_whitespace = 0x400; - - //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. - //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. - //! By default, whitespace is not normalized. - //! If this flag is specified, source text will be modified. - //! Can be combined with other flags by use of | operator. - //! <br><br> - //! See xml_document::parse() function. - const int parse_normalize_whitespace = 0x800; - - // Compound flags - - //! Parse flags which represent default behaviour of the parser. - //! This is always equal to 0, so that all other flags can be simply ored together. - //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. - //! This also means that meaning of each flag is a <i>negation</i> of the default setting. - //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default, - //! and using the flag will disable it. - //! <br><br> - //! See xml_document::parse() function. - const int parse_default = 0; - - //! A combination of parse flags that forbids any modifications of the source text. - //! This also results in faster parsing. However, note that the following will occur: - //! <ul> - //! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li> - //! <li>entities will not be translated</li> - //! <li>whitespace will not be normalized</li> - //! </ul> - //! See xml_document::parse() function. - const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; - - //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. - //! <br><br> - //! See xml_document::parse() function. - const int parse_fastest = parse_non_destructive | parse_no_data_nodes; - - //! A combination of parse flags resulting in largest amount of data being extracted. - //! This usually results in slowest parsing. - //! <br><br> - //! See xml_document::parse() function. - const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; - - /////////////////////////////////////////////////////////////////////// - // Internals - - //! \cond internal - namespace internal - { - - // Struct that contains lookup tables for the parser - // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). - template<int Dummy> - struct lookup_tables - { - static const unsigned char lookup_whitespace[256]; // Whitespace table - static const unsigned char lookup_node_name[256]; // Node name table - static const unsigned char lookup_text[256]; // Text table - static const unsigned char lookup_text_pure_no_ws[256]; // Text table - static const unsigned char lookup_text_pure_with_ws[256]; // Text table - static const unsigned char lookup_attribute_name[256]; // Attribute name table - static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes - static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes - static const unsigned char lookup_digits[256]; // Digits - static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters - }; - - // Find length of the string - template<class Ch> - inline std::size_t measure(const Ch* p) - { - const Ch* tmp = p; - - while (*tmp) - { - ++tmp; - } - - return tmp - p; - } - - // Compare strings for equality - template<class Ch> - inline bool compare(const Ch* p1, std::size_t size1, const Ch* p2, std::size_t size2, bool case_sensitive) - { - if (size1 != size2) - { - return false; - } - - if (case_sensitive) - { - for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) - if (*p1 != *p2) - { - return false; - } - } - else - { - for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) - if (lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p2)]) - { - return false; - } - } - - return true; - } - } - //! \endcond - - /////////////////////////////////////////////////////////////////////// - // Memory pool - - //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. - //! In most cases, you will not need to use this class directly. - //! However, if you need to create nodes manually or modify names/values of nodes, - //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. - //! Not only is this faster than allocating them by using <code>new</code> operator, - //! but also their lifetime will be tied to the lifetime of document, - //! possibly simplyfing memory management. - //! <br><br> - //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. - //! You can also call allocate_string() function to allocate strings. - //! Such strings can then be used as names or values of nodes without worrying about their lifetime. - //! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called, - //! or when the pool is destroyed. - //! <br><br> - //! It is also possible to create a standalone memory_pool, and use it - //! to allocate nodes, whose lifetime will not be tied to any document. - //! <br><br> - //! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory. - //! Until static memory is exhausted, no dynamic memory allocations are done. - //! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each, - //! by using global <code>new[]</code> and <code>delete[]</code> operators. - //! This behaviour can be changed by setting custom allocation routines. - //! Use set_allocator() function to set them. - //! <br><br> - //! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes. - //! This value defaults to the size of pointer on target architecture. - //! <br><br> - //! To obtain absolutely top performance from the parser, - //! it is important that all nodes are allocated from a single, contiguous block of memory. - //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. - //! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code> - //! to obtain best wasted memory to performance compromise. - //! To do it, define their values before rapidxml.hpp file is included. - //! \param Ch Character type of created nodes. - template<class Ch = char> - class memory_pool - { - - public: - - //! \cond internal - typedef void* (alloc_func)(std::size_t); // Type of user-defined function used to allocate memory - typedef void (free_func)(void*); // Type of user-defined function used to free memory - //! \endcond - - //! Constructs empty pool with default allocator functions. - memory_pool() - : m_alloc_func(nullptr) - , m_free_func(nullptr) - { - init(); - } - - //! Destroys pool and frees all the memory. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Nodes allocated from the pool are no longer valid. - ~memory_pool() - { - clear(); - } - - //! Allocates a new node from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param type Type of node to create. - //! \param name Name to assign to the node, or 0 to assign no name. - //! \param value Value to assign to the node, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated node. This pointer will never be NULL. - xml_node<Ch>* allocate_node(node_type type, - const Ch* name = nullptr, const Ch* value = nullptr, - std::size_t name_size = 0, std::size_t value_size = 0) - { - void* memory = allocate_aligned(sizeof(xml_node<Ch>)); - xml_node<Ch>* node = new(memory) xml_node<Ch>(type); - - if (name) - { - if (name_size > 0) - { - node->name(name, name_size); - } - else - { - node->name(name); - } - } - - if (value) - { - if (value_size > 0) - { - node->value(value, value_size); - } - else - { - node->value(value); - } - } - - return node; - } - - //! Allocates a new attribute from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param name Name to assign to the attribute, or 0 to assign no name. - //! \param value Value to assign to the attribute, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated attribute. This pointer will never be NULL. - xml_attribute<Ch>* allocate_attribute(const Ch* name = nullptr, const Ch* value = nullptr, - std::size_t name_size = 0, std::size_t value_size = 0) - { - void* memory = allocate_aligned(sizeof(xml_attribute<Ch>)); - xml_attribute<Ch>* attribute = new(memory) xml_attribute<Ch>; - - if (name) - { - if (name_size > 0) - { - attribute->name(name, name_size); - } - else - { - attribute->name(name); - } - } - - if (value) - { - if (value_size > 0) - { - attribute->value(value, value_size); - } - else - { - attribute->value(value); - } - } - - return attribute; - } - - //! Allocates a char array of given size from the pool, and optionally copies a given string to it. - //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param source String to initialize the allocated memory with, or 0 to not initialize it. - //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. - //! \return Pointer to allocated char array. This pointer will never be NULL. - Ch* allocate_string(const Ch* source = 0, std::size_t size = 0) - { - assert(source || size); // Either source or size (or both) must be specified - - if (size == 0) - { - size = internal::measure(source) + 1; - } - - Ch* result = static_cast<Ch*>(allocate_aligned(size * sizeof(Ch))); - - if (source) - for (std::size_t i = 0; i < size; ++i) - { - result[i] = source[i]; - } - - return result; - } - - //! Clones an xml_node and its hierarchy of child nodes and attributes. - //! Nodes and attributes are allocated from this memory pool. - //! Names and values are not cloned, they are shared between the clone and the source. - //! Result node can be optionally specified as a second parameter, - //! in which case its contents will be replaced with cloned source node. - //! This is useful when you want to clone entire document. - //! \param source Node to clone. - //! \param result Node to put results in, or 0 to automatically allocate result node - //! \return Pointer to cloned node. This pointer will never be NULL. - xml_node<Ch>* clone_node(const xml_node<Ch>* source, xml_node<Ch>* result = 0) - { - // Prepare result node - if (result) - { - result->remove_all_attributes(); - result->remove_all_nodes(); - result->type(source->type()); - } - else - { - result = allocate_node(source->type()); - } - - // Clone name and value - result->name(source->name(), source->name_size()); - result->value(source->value(), source->value_size()); - - // Clone child nodes and attributes - for (xml_node<Ch>* child = source->first_node(); child; child = child->next_sibling()) - { - result->append_node(clone_node(child)); - } - - for (xml_attribute<Ch>* attr = source->first_attribute(); attr; attr = attr->next_attribute()) - { - result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); - } - - return result; - } - - //! Clears the pool. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Any nodes or strings allocated from the pool will no longer be valid. - void clear() - { - while (m_begin != m_static_memory) - { - char* previous_begin = reinterpret_cast<header*>(align(m_begin))->previous_begin; - - if (m_free_func) - { - m_free_func(m_begin); - } - else - { - delete[] m_begin; - } - - m_begin = previous_begin; - } - - init(); - } - - //! Sets or resets the user-defined memory allocation functions for the pool. - //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. - //! Allocation function must not return invalid pointer on failure. It should either throw, - //! stop the program, or use <code>longjmp()</code> function to pass control to other place of program. - //! If it returns invalid pointer, results are undefined. - //! <br><br> - //! User defined allocation functions must have the following forms: - //! <br><code> - //! <br>void *allocate(std::size_t size); - //! <br>void free(void *pointer); - //! </code><br> - //! \param af Allocation function, or 0 to restore default function - //! \param ff Free function, or 0 to restore default function - void set_allocator(alloc_func* af, free_func* ff) - { - assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet - m_alloc_func = af; - m_free_func = ff; - } - - private: - - struct header - { - char* previous_begin; - }; - - void init() - { - m_begin = m_static_memory; - m_ptr = align(m_begin); - m_end = m_static_memory + sizeof(m_static_memory); - } - - char* align(char* ptr) - { - std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); - return ptr + alignment; - } - - char* allocate_raw(std::size_t size) - { - // Allocate - void* memory; - - if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] - { - memory = m_alloc_func(size); - assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp - } - else - { - memory = new char[size]; -#ifdef RAPIDXML_NO_EXCEPTIONS - - if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc - { - RAPIDXML_PARSE_ERROR("out of memory", 0); - } - -#endif - } - - return static_cast<char*>(memory); - } - - void* allocate_aligned(std::size_t size) - { - // Calculate aligned pointer - char* result = align(m_ptr); - - // If not enough memory left in current pool, allocate a new pool - if (result + size > m_end) - { - // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) - std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; - - if (pool_size < size) - { - pool_size = size; - } - - // Allocate - std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation - char* raw_memory = allocate_raw(alloc_size); - - // Setup new pool in allocated memory - char* pool = align(raw_memory); - header* new_header = reinterpret_cast<header*>(pool); - new_header->previous_begin = m_begin; - m_begin = raw_memory; - m_ptr = pool + sizeof(header); - m_end = raw_memory + alloc_size; - - // Calculate aligned pointer again using new pool - result = align(m_ptr); - } - - // Update pool and return aligned pointer - m_ptr = result + size; - return result; - } - - char* m_begin; // Start of raw memory making up current pool - char* m_ptr; // First free byte in current pool - char* m_end; // One past last available byte in current pool - char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory - alloc_func* m_alloc_func; // Allocator function, or 0 if default is to be used - free_func* m_free_func; // Free function, or 0 if default is to be used - }; - - /////////////////////////////////////////////////////////////////////////// - // XML base - - //! Base class for xml_node and xml_attribute implementing common functions: - //! name(), name_size(), value(), value_size() and parent(). - //! \param Ch Character type to use - template<class Ch = char> - class xml_base - { - - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - // Construct a base with empty name, value and parent - xml_base() - : m_name(nullptr) - , m_value(nullptr) - , m_parent(nullptr) - { - } - - /////////////////////////////////////////////////////////////////////////// - // Node data access - - //! Gets name of the node. - //! Interpretation of name depends on type of node. - //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //! <br><br> - //! Use name_size() function to determine length of the name. - //! \return Name of node, or empty string if node has no name. - Ch* name() const - { - return m_name ? m_name : nullstr(); - } - - //! Gets size of node name, not including terminator character. - //! This function works correctly irrespective of whether name is or is not zero terminated. - //! \return Size of node name, in characters. - std::size_t name_size() const - { - return m_name ? m_name_size : 0; - } - - //! Gets value of node. - //! Interpretation of value depends on type of node. - //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //! <br><br> - //! Use value_size() function to determine length of the value. - //! \return Value of node, or empty string if node has no value. - Ch* value() const - { - return m_value ? m_value : nullstr(); - } - - //! Gets size of node value, not including terminator character. - //! This function works correctly irrespective of whether value is or is not zero terminated. - //! \return Size of node value, in characters. - std::size_t value_size() const - { - return m_value ? m_value_size : 0; - } - - /////////////////////////////////////////////////////////////////////////// - // Node modification - - //! Sets name of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //! <br><br> - //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //! <br><br> - //! Size of name must be specified separately, because name does not have to be zero terminated. - //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //! \param name Name of node to set. Does not have to be zero terminated. - //! \param size Size of name, in characters. This does not include zero terminator, if one is present. - void name(const Ch* name, std::size_t size) - { - m_name = const_cast<Ch*>(name); - m_name_size = size; - } - - //! Sets name of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). - //! \param name Name of node to set. Must be zero terminated. - void name(const Ch* name) - { - this->name(name, internal::measure(name)); - } - - //! Sets value of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //! <br><br> - //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //! <br><br> - //! Size of value must be specified separately, because it does not have to be zero terminated. - //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //! <br><br> - //! If an element has a child node of type node_data, it will take precedence over element value when printing. - //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. - //! \param value value of node to set. Does not have to be zero terminated. - //! \param size Size of value, in characters. This does not include zero terminator, if one is present. - void value(const Ch* value, std::size_t size) - { - m_value = const_cast<Ch*>(value); - m_value_size = size; - } - - //! Sets value of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). - //! \param value Vame of node to set. Must be zero terminated. - void value(const Ch* value) - { - this->value(value, internal::measure(value)); - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets node parent. - //! \return Pointer to parent node, or 0 if there is no parent. - xml_node<Ch>* parent() const - { - return m_parent; - } - - protected: - - // Return empty string - static Ch* nullstr() - { - static Ch zero = Ch('\0'); - return &zero; - } - - Ch* m_name; // Name of node, or 0 if no name - Ch* m_value; // Value of node, or 0 if no value - std::size_t m_name_size; // Length of node name, or undefined of no name - std::size_t m_value_size; // Length of node value, or undefined if no value - xml_node<Ch>* m_parent; // Pointer to parent node, or 0 if none - - }; - - //! Class representing attribute node of XML document. - //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). - //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. - //! Thus, this text must persist in memory for the lifetime of attribute. - //! \param Ch Character type to use. - template<class Ch = char> - class xml_attribute: public xml_base<Ch> - { - - friend class xml_node<Ch>; - - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - //! Constructs an empty attribute with the specified type. - //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. - xml_attribute() - { - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets document of which attribute is a child. - //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. - xml_document<Ch>* document() const - { - if (xml_node<Ch>* node = this->parent()) - { - while (node->parent()) - { - node = node->parent(); - } - - return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0; - } - else - { - return 0; - } - } - - //! Gets previous attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute<Ch>* previous_attribute(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_attribute<Ch>* attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - { - return attribute; - } - - return 0; - } - else - { - return this->m_parent ? m_prev_attribute : 0; - } - } - - //! Gets next attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute<Ch>* next_attribute(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_attribute<Ch>* attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - { - return attribute; - } - - return nullptr; - } - else - { - return this->m_parent ? m_next_attribute : nullptr; - } - } - - private: - - xml_attribute<Ch>* m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero - xml_attribute<Ch>* m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero - - }; - - /////////////////////////////////////////////////////////////////////////// - // XML node - - //! Class representing a node of XML document. - //! Each node may have associated name and value strings, which are available through name() and value() functions. - //! Interpretation of name and value depends on type of the node. - //! Type of node can be determined by using type() function. - //! <br><br> - //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. - //! Thus, this text must persist in the memory for the lifetime of node. - //! \param Ch Character type to use. - template<class Ch = char> - class xml_node: public xml_base<Ch> - { - - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - //! Constructs an empty node with the specified type. - //! Consider using memory_pool of appropriate document to allocate nodes manually. - //! \param type Type of node to construct. - xml_node(node_type type) - : m_type(type) - , m_first_node(nullptr) - , m_first_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////////////// - // Node data access - - //! Gets type of node. - //! \return Type of node. - node_type type() const - { - return m_type; - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets document of which node is a child. - //! \return Pointer to document that contains this node, or 0 if there is no parent document. - xml_document<Ch>* document() const - { - xml_node<Ch>* node = const_cast<xml_node<Ch> *>(this); - - while (node->parent()) - { - node = node->parent(); - } - - return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0; - } - - //! Gets first child node, optionally matching node name. - //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node<Ch>* first_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_node<Ch>* child = m_first_node; child; child = child->next_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - { - return child; - } - - return nullptr; - } - else - { - return m_first_node; - } - } - - //! Gets last child node, optionally matching node name. - //! Behaviour is undefined if node has no children. - //! Use first_node() to test if node has children. - //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node<Ch>* last_node(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(m_first_node); // Cannot query for last child if node has no children - - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_node<Ch>* child = m_last_node; child; child = child->previous_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - { - return child; - } - - return 0; - } - else - { - return m_last_node; - } - } - - //! Gets previous sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node<Ch>* previous_sibling(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_node<Ch>* sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - { - return sibling; - } - - return 0; - } - else - { - return m_prev_sibling; - } - } - - //! Gets next sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node<Ch>* next_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_node<Ch>* sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - { - return sibling; - } - - return nullptr; - } - else - { - return m_next_sibling; - } - } - - //! Gets first attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute<Ch>* first_attribute(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_attribute<Ch>* attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - { - return attribute; - } - - return nullptr; - } - else - { - return m_first_attribute; - } - } - - //! Gets last attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute<Ch>* last_attribute(const Ch* name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - { - name_size = internal::measure(name); - } - - for (xml_attribute<Ch>* attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - { - return attribute; - } - - return 0; - } - else - { - return m_first_attribute ? m_last_attribute : 0; - } - } - - /////////////////////////////////////////////////////////////////////////// - // Node modification - - //! Sets type of node. - //! \param type Type of node to set. - void type(node_type type) - { - m_type = type; - } - - /////////////////////////////////////////////////////////////////////////// - // Node manipulation - - //! Prepends a new child node. - //! The prepended child becomes the first child, and all existing children are moved one position back. - //! \param child Node to prepend. - void prepend_node(xml_node<Ch>* child) - { - assert(child && !child->parent() && child->type() != node_document); - - if (first_node()) - { - child->m_next_sibling = m_first_node; - m_first_node->m_prev_sibling = child; - } - else - { - child->m_next_sibling = 0; - m_last_node = child; - } - - m_first_node = child; - child->m_parent = this; - child->m_prev_sibling = 0; - } - - //! Appends a new child node. - //! The appended child becomes the last child. - //! \param child Node to append. - void append_node(xml_node<Ch>* child) - { - assert(child && !child->parent() && child->type() != node_document); - - if (first_node()) - { - child->m_prev_sibling = m_last_node; - m_last_node->m_next_sibling = child; - } - else - { - child->m_prev_sibling = nullptr; - m_first_node = child; - } - - m_last_node = child; - child->m_parent = this; - child->m_next_sibling = nullptr; - } - - //! Inserts a new child node at specified place inside the node. - //! All children after and including the specified node are moved one position back. - //! \param where Place where to insert the child, or 0 to insert at the back. - //! \param child Node to insert. - void insert_node(xml_node<Ch>* where, xml_node<Ch>* child) - { - assert(!where || where->parent() == this); - assert(child && !child->parent() && child->type() != node_document); - - if (where == m_first_node) - { - prepend_node(child); - } - else if (where == 0) - { - append_node(child); - } - else - { - child->m_prev_sibling = where->m_prev_sibling; - child->m_next_sibling = where; - where->m_prev_sibling->m_next_sibling = child; - where->m_prev_sibling = child; - child->m_parent = this; - } - } - - //! Removes first child node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_first_node() - { - assert(first_node()); - xml_node<Ch>* child = m_first_node; - m_first_node = child->m_next_sibling; - - if (child->m_next_sibling) - { - child->m_next_sibling->m_prev_sibling = 0; - } - else - { - m_last_node = 0; - } - - child->m_parent = 0; - } - - //! Removes last child of the node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_last_node() - { - assert(first_node()); - xml_node<Ch>* child = m_last_node; - - if (child->m_prev_sibling) - { - m_last_node = child->m_prev_sibling; - child->m_prev_sibling->m_next_sibling = 0; - } - else - { - m_first_node = 0; - } - - child->m_parent = 0; - } - - //! Removes specified child from the node - // \param where Pointer to child to be removed. - void remove_node(xml_node<Ch>* where) - { - assert(where && where->parent() == this); - assert(first_node()); - - if (where == m_first_node) - { - remove_first_node(); - } - else if (where == m_last_node) - { - remove_last_node(); - } - else - { - where->m_prev_sibling->m_next_sibling = where->m_next_sibling; - where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; - where->m_parent = 0; - } - } - - //! Removes all child nodes (but not attributes). - void remove_all_nodes() - { - for (xml_node<Ch>* node = first_node(); node; node = node->m_next_sibling) - { - node->m_parent = nullptr; - } - - m_first_node = nullptr; - } - - //! Prepends a new attribute to the node. - //! \param attribute Attribute to prepend. - void prepend_attribute(xml_attribute<Ch>* attribute) - { - assert(attribute && !attribute->parent()); - - if (first_attribute()) - { - attribute->m_next_attribute = m_first_attribute; - m_first_attribute->m_prev_attribute = attribute; - } - else - { - attribute->m_next_attribute = 0; - m_last_attribute = attribute; - } - - m_first_attribute = attribute; - attribute->m_parent = this; - attribute->m_prev_attribute = 0; - } - - //! Appends a new attribute to the node. - //! \param attribute Attribute to append. - void append_attribute(xml_attribute<Ch>* attribute) - { - assert(attribute && !attribute->parent()); - - if (first_attribute()) - { - attribute->m_prev_attribute = m_last_attribute; - m_last_attribute->m_next_attribute = attribute; - } - else - { - attribute->m_prev_attribute = nullptr; - m_first_attribute = attribute; - } - - m_last_attribute = attribute; - attribute->m_parent = this; - attribute->m_next_attribute = nullptr; - } - - //! Inserts a new attribute at specified place inside the node. - //! All attributes after and including the specified attribute are moved one position back. - //! \param where Place where to insert the attribute, or 0 to insert at the back. - //! \param attribute Attribute to insert. - void insert_attribute(xml_attribute<Ch>* where, xml_attribute<Ch>* attribute) - { - assert(!where || where->parent() == this); - assert(attribute && !attribute->parent()); - - if (where == m_first_attribute) - { - prepend_attribute(attribute); - } - else if (where == 0) - { - append_attribute(attribute); - } - else - { - attribute->m_prev_attribute = where->m_prev_attribute; - attribute->m_next_attribute = where; - where->m_prev_attribute->m_next_attribute = attribute; - where->m_prev_attribute = attribute; - attribute->m_parent = this; - } - } - - //! Removes first attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_first_attribute() - { - assert(first_attribute()); - xml_attribute<Ch>* attribute = m_first_attribute; - - if (attribute->m_next_attribute) - { - attribute->m_next_attribute->m_prev_attribute = 0; - } - else - { - m_last_attribute = 0; - } - - attribute->m_parent = 0; - m_first_attribute = attribute->m_next_attribute; - } - - //! Removes last attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_last_attribute() - { - assert(first_attribute()); - xml_attribute<Ch>* attribute = m_last_attribute; - - if (attribute->m_prev_attribute) - { - attribute->m_prev_attribute->m_next_attribute = 0; - m_last_attribute = attribute->m_prev_attribute; - } - else - { - m_first_attribute = 0; - } - - attribute->m_parent = 0; - } - - //! Removes specified attribute from node. - //! \param where Pointer to attribute to be removed. - void remove_attribute(xml_attribute<Ch>* where) - { - assert(first_attribute() && where->parent() == this); - - if (where == m_first_attribute) - { - remove_first_attribute(); - } - else if (where == m_last_attribute) - { - remove_last_attribute(); - } - else - { - where->m_prev_attribute->m_next_attribute = where->m_next_attribute; - where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; - where->m_parent = 0; - } - } - - //! Removes all attributes of node. - void remove_all_attributes() - { - for (xml_attribute<Ch>* attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) - { - attribute->m_parent = nullptr; - } - - m_first_attribute = nullptr; - } - - private: - - /////////////////////////////////////////////////////////////////////////// - // Restrictions - - // No copying - xml_node(const xml_node&); - void operator =(const xml_node&); - - /////////////////////////////////////////////////////////////////////////// - // Data members - - // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. - // This is required for maximum performance, as it allows the parser to omit initialization of - // unneded/redundant values. - // - // The rules are as follows: - // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively - // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage - // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage - - node_type m_type; // Type of node; always valid - xml_node<Ch>* m_first_node; // Pointer to first child node, or 0 if none; always valid - xml_node<Ch>* m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero - xml_attribute<Ch>* m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid - xml_attribute<Ch>* m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero - xml_node<Ch>* m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - xml_node<Ch>* m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - - }; - - /////////////////////////////////////////////////////////////////////////// - // XML document - - //! This class represents root of the DOM hierarchy. - //! It is also an xml_node and a memory_pool through public inheritance. - //! Use parse() function to build a DOM tree from a zero-terminated XML text string. - //! parse() function allocates memory for nodes and attributes by using functions of xml_document, - //! which are inherited from memory_pool. - //! To access root node of the document, use the document itself, as if it was an xml_node. - //! \param Ch Character type to use. - template<class Ch = char> - class xml_document: public xml_node<Ch>, public memory_pool<Ch> - { - - public: - - //! Constructs empty XML document - xml_document() - : xml_node<Ch>(node_document) - { - } - - //! Parses zero-terminated XML string according to given flags. - //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. - //! The string must persist for the lifetime of the document. - //! In case of error, rapidxml::parse_error exception will be thrown. - //! <br><br> - //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. - //! Make sure that data is zero-terminated. - //! <br><br> - //! Document can be parsed into multiple times. - //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. - //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. - template<int Flags> - void parse(Ch* text) - { - assert(text); - - // Remove current contents - this->remove_all_nodes(); - this->remove_all_attributes(); - - // Parse BOM, if any - parse_bom<Flags>(text); - - // Parse children - while (1) - { - // Skip whitespace before node - skip<whitespace_pred, Flags>(text); - - if (*text == 0) - { - break; - } - - // Parse and append new child - if (*text == Ch('<')) - { - ++text; // Skip '<' - - if (xml_node<Ch>* node = parse_node<Flags>(text)) - { - this->append_node(node); - } - } - else - { - RAPIDXML_PARSE_ERROR("expected <", text); - } - } - - } - - //! Clears the document by deleting all nodes and clearing the memory pool. - //! All nodes owned by document pool are destroyed. - void clear() - { - this->remove_all_nodes(); - this->remove_all_attributes(); - memory_pool<Ch>::clear(); - } - - private: - - /////////////////////////////////////////////////////////////////////// - // Internal character utility functions - - // Detect whitespace character - struct whitespace_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_whitespace[static_cast<unsigned char>(ch)]; - } - }; - - // Detect node name character - struct node_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_node_name[static_cast<unsigned char>(ch)]; - } - }; - - // Detect attribute name character - struct attribute_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_attribute_name[static_cast<unsigned char>(ch)]; - } - }; - - // Detect text character (PCDATA) - struct text_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text[static_cast<unsigned char>(ch)]; - } - }; - - // Detect text character (PCDATA) that does not require processing - struct text_pure_no_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast<unsigned char>(ch)]; - } - }; - - // Detect text character (PCDATA) that does not require processing - struct text_pure_with_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast<unsigned char>(ch)]; - } - }; - - // Detect attribute value character - template<Ch Quote> - struct attribute_value_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - { - return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast<unsigned char>(ch)]; - } - - if (Quote == Ch('\"')) - { - return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast<unsigned char>(ch)]; - } - - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; - - // Detect attribute value character - template<Ch Quote> - struct attribute_value_pure_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - { - return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast<unsigned char>(ch)]; - } - - if (Quote == Ch('\"')) - { - return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast<unsigned char>(ch)]; - } - - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; - - // Insert coded character, using UTF8 or 8-bit ASCII - template<int Flags> - static void insert_coded_character(Ch*& text, unsigned long code) - { - if (Flags & parse_no_utf8) - { - // Insert 8-bit ASCII character - // Todo: possibly verify that code is less than 256 and use replacement char otherwise? - text[0] = static_cast<unsigned char>(code); - text += 1; - } - else - { - // Insert UTF8 sequence - if (code < 0x80) // 1 byte sequence - { - text[0] = static_cast<unsigned char>(code); - text += 1; - } - else if (code < 0x800) // 2 byte sequence - { - text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast<unsigned char>(code | 0xC0); - text += 2; - } - else if (code < 0x10000) // 3 byte sequence - { - text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast<unsigned char>(code | 0xE0); - text += 3; - } - else if (code < 0x110000) // 4 byte sequence - { - text[3] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast<unsigned char>(code | 0xF0); - text += 4; - } - else // Invalid, only codes up to 0x10FFFF are allowed in Unicode - { - RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); - } - } - } - - // Skip characters until predicate evaluates to true - template<class StopPred, int Flags> - static void skip(Ch*& text) - { - Ch* tmp = text; - - while (StopPred::test(*tmp)) - { - ++tmp; - } - - text = tmp; - } - - // Skip characters until predicate evaluates to true while doing the following: - // - replacing XML character entity references with proper characters (' & " < > &#...;) - // - condensing whitespace sequences to single space character - template<class StopPred, class StopPredPure, int Flags> - static Ch* skip_and_expand_character_refs(Ch*& text) - { - // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip - if (Flags & parse_no_entity_translation && - !(Flags & parse_normalize_whitespace) && - !(Flags & parse_trim_whitespace)) - { - skip<StopPred, Flags>(text); - return text; - } - - // Use simple skip until first modification is detected - skip<StopPredPure, Flags>(text); - - // Use translation skip - Ch* src = text; - Ch* dest = src; - - while (StopPred::test(*src)) - { - // If entity translation is enabled - if (!(Flags & parse_no_entity_translation)) - { - // Test if replacement is needed - if (src[0] == Ch('&')) - { - switch (src[1]) - { - - // & ' - case Ch('a'): - if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) - { - *dest = Ch('&'); - ++dest; - src += 5; - continue; - } - - if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) - { - *dest = Ch('\''); - ++dest; - src += 6; - continue; - } - - break; - - // " - case Ch('q'): - if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) - { - *dest = Ch('"'); - ++dest; - src += 6; - continue; - } - - break; - - // > - case Ch('g'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('>'); - ++dest; - src += 4; - continue; - } - - break; - - // < - case Ch('l'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('<'); - ++dest; - src += 4; - continue; - } - - break; - - // &#...; - assumes ASCII - case Ch('#'): - if (src[2] == Ch('x')) - { - unsigned long code = 0; - src += 3; // Skip &#x - - while (1) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)]; - - if (digit == 0xFF) - { - break; - } - - code = code * 16 + digit; - ++src; - } - - insert_coded_character<Flags>(dest, code); // Put character in output - } - else - { - unsigned long code = 0; - src += 2; // Skip &# - - while (1) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)]; - - if (digit == 0xFF) - { - break; - } - - code = code * 10 + digit; - ++src; - } - - insert_coded_character<Flags>(dest, code); // Put character in output - } - - if (*src == Ch(';')) - { - ++src; - } - else - { - RAPIDXML_PARSE_ERROR("expected ;", src); - } - - continue; - - // Something else - default: - // Ignore, just copy '&' verbatim - break; - - } - } - } - - // If whitespace condensing is enabled - if (Flags & parse_normalize_whitespace) - { - // Test if condensing is needed - if (whitespace_pred::test(*src)) - { - *dest = Ch(' '); - ++dest; // Put single space in dest - ++src; // Skip first whitespace char - - // Skip remaining whitespace chars - while (whitespace_pred::test(*src)) - { - ++src; - } - - continue; - } - } - - // No replacement, only copy character - *dest++ = *src++; - - } - - // Return new end - text = src; - return dest; - - } - - /////////////////////////////////////////////////////////////////////// - // Internal parsing functions - - // Parse BOM, if any - template<int Flags> - void parse_bom(Ch*& text) - { - // UTF-8? - if (static_cast<unsigned char>(text[0]) == 0xEF && - static_cast<unsigned char>(text[1]) == 0xBB && - static_cast<unsigned char>(text[2]) == 0xBF) - { - text += 3; // Skup utf-8 bom - } - } - - // Parse XML declaration (<?xml...) - template<int Flags> - xml_node<Ch>* parse_xml_declaration(Ch*& text) - { - // If parsing of declaration is disabled - if (!(Flags & parse_declaration_node)) - { - // Skip until end of declaration - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (!text[0]) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - text += 2; // Skip '?>' - return nullptr; - } - - // Create declaration - xml_node<Ch>* declaration = this->allocate_node(node_declaration); - - // Skip whitespace before attributes or ?> - skip<whitespace_pred, Flags>(text); - - // Parse declaration attributes - parse_node_attributes<Flags>(text, declaration); - - // Skip ?> - if (text[0] != Ch('?') || text[1] != Ch('>')) - { - RAPIDXML_PARSE_ERROR("expected ?>", text); - } - - text += 2; - - return declaration; - } - - // Parse XML comment (<!--...) - template<int Flags> - xml_node<Ch>* parse_comment(Ch*& text) - { - // If parsing of comments is disabled - if (!(Flags & parse_comment_nodes)) - { - // Skip until end of comment - while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) - { - if (!text[0]) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - text += 3; // Skip '-->' - return nullptr; // Do not produce comment node - } - - // Remember value start - Ch* value = text; - - // Skip until end of comment - while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) - { - if (!text[0]) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - // Create comment node - xml_node<Ch>* comment = this->allocate_node(node_comment); - comment->value(value, text - value); - - // Place zero terminator after comment value - if (!(Flags & parse_no_string_terminators)) - { - *text = Ch('\0'); - } - - text += 3; // Skip '-->' - return comment; - } - - // Parse DOCTYPE - template<int Flags> - xml_node<Ch>* parse_doctype(Ch*& text) - { - // Remember value start - Ch* value = text; - - // Skip to > - while (*text != Ch('>')) - { - // Determine character type - switch (*text) - { - - // If '[' encountered, scan for matching ending ']' using naive algorithm with depth - // This works for all W3C test files except for 2 most wicked - case Ch('['): - { - ++text; // Skip '[' - int depth = 1; - - while (depth > 0) - { - switch (*text) - { - case Ch('['): - ++depth; - break; - - case Ch(']'): - --depth; - break; - - case 0: - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - break; - } - - // Error on end of text - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - - // Other character, skip it - default: - ++text; - - } - } - - // If DOCTYPE nodes enabled - if (Flags & parse_doctype_node) - { - // Create a new doctype node - xml_node<Ch>* doctype = this->allocate_node(node_doctype); - doctype->value(value, text - value); - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - { - *text = Ch('\0'); - } - - text += 1; // skip '>' - return doctype; - } - else - { - text += 1; // skip '>' - return nullptr; - } - - } - - // Parse PI - template<int Flags> - xml_node<Ch>* parse_pi(Ch*& text) - { - // If creation of PI nodes is enabled - if (Flags & parse_pi_nodes) - { - // Create pi node - xml_node<Ch>* pi = this->allocate_node(node_pi); - - // Extract PI target name - Ch* name = text; - skip<node_name_pred, Flags>(text); - - if (text == name) - { - RAPIDXML_PARSE_ERROR("expected PI target", text); - } - - pi->name(name, text - name); - - // Skip whitespace between pi target and pi - skip<whitespace_pred, Flags>(text); - - // Remember start of pi - Ch* value = text; - - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - // Set pi value (verbatim, no entity expansion or whitespace normalization) - pi->value(value, text - value); - - // Place zero terminator after name and value - if (!(Flags & parse_no_string_terminators)) - { - pi->name()[pi->name_size()] = Ch('\0'); - pi->value()[pi->value_size()] = Ch('\0'); - } - - text += 2; // Skip '?>' - return pi; - } - else - { - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - text += 2; // Skip '?>' - return nullptr; - } - } - - // Parse and append data - // Return character that ends data. - // This is necessary because this character might have been overwritten by a terminating 0 - template<int Flags> - Ch parse_and_append_data(xml_node<Ch>* node, Ch*& text, Ch* contents_start) - { - // Backup to contents start if whitespace trimming is disabled - if (!(Flags & parse_trim_whitespace)) - { - text = contents_start; - } - - // Skip until end of data - Ch* value = text, *end; - - if (Flags & parse_normalize_whitespace) - { - end = skip_and_expand_character_refs<text_pred, text_pure_with_ws_pred, Flags>(text); - } - else - { - end = skip_and_expand_character_refs<text_pred, text_pure_no_ws_pred, Flags>(text); - } - - // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > - if (Flags & parse_trim_whitespace) - { - if (Flags & parse_normalize_whitespace) - { - // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end - if (*(end - 1) == Ch(' ')) - { - --end; - } - } - else - { - // Backup until non-whitespace character is found - while (whitespace_pred::test(*(end - 1))) - { - --end; - } - } - } - - // If characters are still left between end and value (this test is only necessary if normalization is enabled) - // Create new data node - if (!(Flags & parse_no_data_nodes)) - { - xml_node<Ch>* data = this->allocate_node(node_data); - data->value(value, end - value); - node->append_node(data); - } - - // Add data to parent node if no data exists yet - if (!(Flags & parse_no_element_values)) - if (*node->value() == Ch('\0')) - { - node->value(value, end - value); - } - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - { - Ch ch = *text; - *end = Ch('\0'); - return ch; // Return character that ends data; this is required because zero terminator overwritten it - } - - // Return character that ends data - return *text; - } - - // Parse CDATA - template<int Flags> - xml_node<Ch>* parse_cdata(Ch*& text) - { - // If CDATA is disabled - if (Flags & parse_no_data_nodes) - { - // Skip until end of cdata - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - text += 3; // Skip ]]> - return nullptr; // Do not produce CDATA node - } - - // Skip until end of cdata - Ch* value = text; - - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - // Create new cdata node - xml_node<Ch>* cdata = this->allocate_node(node_cdata); - cdata->value(value, text - value); - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - { - *text = Ch('\0'); - } - - text += 3; // Skip ]]> - return cdata; - } - - // Parse element node - template<int Flags> - xml_node<Ch>* parse_element(Ch*& text) - { - // Create element node - xml_node<Ch>* element = this->allocate_node(node_element); - - // Extract element name - Ch* name = text; - skip<node_name_pred, Flags>(text); - - if (text == name) - { - RAPIDXML_PARSE_ERROR("expected element name", text); - } - - element->name(name, text - name); - - // Skip whitespace between element name and attributes or > - skip<whitespace_pred, Flags>(text); - - // Parse attributes, if any - parse_node_attributes<Flags>(text, element); - - // Determine ending type - if (*text == Ch('>')) - { - ++text; - parse_node_contents<Flags>(text, element); - } - else if (*text == Ch('/')) - { - ++text; - - if (*text != Ch('>')) - { - RAPIDXML_PARSE_ERROR("expected >", text); - } - - ++text; - } - else - { - RAPIDXML_PARSE_ERROR("expected >", text); - } - - // Place zero terminator after name - if (!(Flags & parse_no_string_terminators)) - { - element->name()[element->name_size()] = Ch('\0'); - } - - // Return parsed element - return element; - } - - // Determine node type, and parse it - template<int Flags> - xml_node<Ch>* parse_node(Ch*& text) - { - // Parse proper node type - switch (text[0]) - { - - // <... - default: - // Parse and append element node - return parse_element<Flags>(text); - - // <?... - case Ch('?'): - ++text; // Skip ? - - if ((text[0] == Ch('x') || text[0] == Ch('X')) && - (text[1] == Ch('m') || text[1] == Ch('M')) && - (text[2] == Ch('l') || text[2] == Ch('L')) && - whitespace_pred::test(text[3])) - { - // '<?xml ' - xml declaration - text += 4; // Skip 'xml ' - return parse_xml_declaration<Flags>(text); - } - else - { - // Parse PI - return parse_pi<Flags>(text); - } - - // <!... - case Ch('!'): - - // Parse proper subset of <! node - switch (text[1]) - { - - // <!- - case Ch('-'): - if (text[2] == Ch('-')) - { - // '<!--' - xml comment - text += 3; // Skip '!--' - return parse_comment<Flags>(text); - } - - break; - - // <![ - case Ch('['): - if (text[2] == Ch('C') && text[3] == Ch('D') && text[4] == Ch('A') && - text[5] == Ch('T') && text[6] == Ch('A') && text[7] == Ch('[')) - { - // '<![CDATA[' - cdata - text += 8; // Skip '![CDATA[' - return parse_cdata<Flags>(text); - } - - break; - - // <!D - case Ch('D'): - if (text[2] == Ch('O') && text[3] == Ch('C') && text[4] == Ch('T') && - text[5] == Ch('Y') && text[6] == Ch('P') && text[7] == Ch('E') && - whitespace_pred::test(text[8])) - { - // '<!DOCTYPE ' - doctype - text += 9; // skip '!DOCTYPE ' - return parse_doctype<Flags>(text); - } - - } // switch - - // Attempt to skip other, unrecognized node types starting with <! - ++text; // Skip ! - - while (*text != Ch('>')) - { - if (*text == 0) - { - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - - ++text; - } - - ++text; // Skip '>' - return nullptr; // No node recognized - - } - } - - // Parse contents of the node - children, data etc. - template<int Flags> - void parse_node_contents(Ch*& text, xml_node<Ch>* node) - { - // For all children and text - while (1) - { - // Skip whitespace between > and node contents - Ch* contents_start = text; // Store start of node contents before whitespace is skipped - skip<whitespace_pred, Flags>(text); - Ch next_char = *text; - - // After data nodes, instead of continuing the loop, control jumps here. - // This is because zero termination inside parse_and_append_data() function - // would wreak havoc with the above code. - // Also, skipping whitespace after data nodes is unnecessary. -after_data_node: - - // Determine what comes next: node closing, child node, data node, or 0? - switch (next_char) - { - - // Node closing or child node - case Ch('<'): - if (text[1] == Ch('/')) - { - // Node closing - text += 2; // Skip '</' - - if (Flags & parse_validate_closing_tags) - { - // Skip and validate closing tag name - Ch* closing_name = text; - skip<node_name_pred, Flags>(text); - - if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) - { - RAPIDXML_PARSE_ERROR("invalid closing tag name", text); - } - } - else - { - // No validation, just skip name - skip<node_name_pred, Flags>(text); - } - - // Skip remaining whitespace after node name - skip<whitespace_pred, Flags>(text); - - if (*text != Ch('>')) - { - RAPIDXML_PARSE_ERROR("expected >", text); - } - - ++text; // Skip '>' - return; // Node closed, finished parsing contents - } - else - { - // Child node - ++text; // Skip '<' - - if (xml_node<Ch>* child = parse_node<Flags>(text)) - { - node->append_node(child); - } - } - - break; - - // End of data - error - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - - // Data node - default: - next_char = parse_and_append_data<Flags>(node, text, contents_start); - goto after_data_node; // Bypass regular processing after data nodes - - } - } - } - - // Parse XML attributes of the node - template<int Flags> - void parse_node_attributes(Ch*& text, xml_node<Ch>* node) - { - // For all attributes - while (attribute_name_pred::test(*text)) - { - // Extract attribute name - Ch* name = text; - ++text; // Skip first character of attribute name - skip<attribute_name_pred, Flags>(text); - - if (text == name) - { - RAPIDXML_PARSE_ERROR("expected attribute name", name); - } - - // Create new attribute - xml_attribute<Ch>* attribute = this->allocate_attribute(); - attribute->name(name, text - name); - node->append_attribute(attribute); - - // Skip whitespace after attribute name - skip<whitespace_pred, Flags>(text); - - // Skip = - if (*text != Ch('=')) - { - RAPIDXML_PARSE_ERROR("expected =", text); - } - - ++text; - - // Add terminating zero after name - if (!(Flags & parse_no_string_terminators)) - { - attribute->name()[attribute->name_size()] = 0; - } - - // Skip whitespace after = - skip<whitespace_pred, Flags>(text); - - // Skip quote and remember if it was ' or " - Ch quote = *text; - - if (quote != Ch('\'') && quote != Ch('"')) - { - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - } - - ++text; - - // Extract attribute value and expand char refs in it - Ch* value = text, *end; - const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes - - if (quote == Ch('\'')) - { - end = skip_and_expand_character_refs < attribute_value_pred < Ch('\'') >, attribute_value_pure_pred < Ch('\'') >, AttFlags > (text); - } - else - { - end = skip_and_expand_character_refs < attribute_value_pred < Ch('"') >, attribute_value_pure_pred < Ch('"') >, AttFlags > (text); - } - - // Set attribute value - attribute->value(value, end - value); - - // Make sure that end quote is present - if (*text != quote) - { - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - } - - ++text; // Skip quote - - // Add terminating zero after value - if (!(Flags & parse_no_string_terminators)) - { - attribute->value()[attribute->value_size()] = 0; - } - - // Skip whitespace after attribute value - skip<whitespace_pred, Flags>(text); - } - } - - }; - - //! \cond internal - namespace internal - { - - // Whitespace (space \n \r \t) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_whitespace[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F - }; - - // Node name (anything but space \n \r \t / > ? \0) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_node_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) (anything but < \0) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_text[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled - // (anything but < \0 &) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_text_pure_no_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled - // (anything but < \0 & space \n \r \t) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_text_pure_with_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute name (anything but space \n \r \t / < > = ? ! \0) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_attribute_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with single quote (anything but ' \0) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with single quote that does not require processing (anything but ' \0 &) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with double quote (anything but " \0) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with double quote that does not require processing (anything but " \0 &) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Digits (dec and hex, 255 denotes end of numeric character reference) - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_digits[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, // 3 - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 4 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 5 - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 6 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 7 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 8 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 9 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // A - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // B - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // C - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // D - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // E - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 // F - }; - - // Upper case conversion - template<int Dummy> - const unsigned char lookup_tables<Dummy>::lookup_upcase[256] = - { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 - 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, // 7 - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 8 - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 9 - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // A - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // B - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // C - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // D - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // E - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // F - }; - } - //! \endcond - -} - -// Undefine internal macros -#undef RAPIDXML_PARSE_ERROR - -// On MSVC, restore warnings state -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif /*__RAPIDXML_H_INCLUDE__*/ +#include "SimoxUtility/xml/rapidxml/rapidxml.hpp"