diff options
-rw-r--r-- | CMakeLists.txt | 12 | ||||
-rwxr-xr-x | lib/download_dependencies.sh | 10 | ||||
-rw-r--r-- | lib/utf8cpp/utf8.h | 34 | ||||
-rw-r--r-- | lib/utf8cpp/utf8/checked.h | 327 | ||||
-rw-r--r-- | lib/utf8cpp/utf8/core.h | 329 | ||||
-rw-r--r-- | lib/utf8cpp/utf8/unchecked.h | 228 | ||||
-rw-r--r-- | src/core/common/Variant.cpp | 14 | ||||
-rw-r--r-- | src/core/common/VariantReader.cpp | 157 | ||||
-rw-r--r-- | src/core/managed/Managed.hpp | 25 | ||||
-rw-r--r-- | src/core/managed/ManagedType.hpp | 119 | ||||
-rw-r--r-- | src/core/managed/Rtti.cpp (renamed from src/core/managed/ManagedType.cpp) | 32 | ||||
-rw-r--r-- | src/core/managed/Rtti.hpp | 183 | ||||
-rw-r--r-- | src/core/model/Typesystem.cpp | 51 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 171 | ||||
-rw-r--r-- | test/core/common/VariantReaderTest.cpp | 65 | ||||
-rw-r--r-- | test/core/managed/ManagedTest.cpp | 36 | ||||
-rw-r--r-- | test/core/managed/RttiTest.cpp | 63 | ||||
-rw-r--r-- | test/core/model/TypesystemTest.cpp | 104 |
18 files changed, 1661 insertions, 299 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 077ac47..371ffca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,12 +32,17 @@ OPTION(BUILD_DOCUMENTATION "Create and install the HTML based API documentation # Enable C++11 and all warnings SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic-errors -std=c++11") -# Include boost (filesystem) expat and mozjs-24 via PkgConfig +# Include expat and mozjs-24 via PkgConfig FIND_PACKAGE(PkgConfig REQUIRED) PKG_CHECK_MODULES(MOZJS REQUIRED mozjs-24) PKG_CHECK_MODULES(EXPAT REQUIRED expat) + +# Include required Boost components using the Boost cmake package FIND_PACKAGE(Boost COMPONENTS system filesystem REQUIRED) +# Set utf8cpp include path +SET(UTF8CPP_INCLUDE_DIR "lib/utf8") + ################################################################################ # Inclusion of doxygen # ################################################################################ @@ -87,6 +92,7 @@ INCLUDE_DIRECTORIES( ${MOZJS_INCLUDE_DIRS} ${EXPAT_INCLUDE_DIRS} ${Boost_INCLUDE_DIR} + ${UTF8CPP_INCLUDE_DIR} ) # Link directories @@ -112,8 +118,8 @@ ADD_LIBRARY(ousia_core src/core/common/Variant src/core/common/VariantReader src/core/managed/Managed - src/core/managed/ManagedType src/core/managed/Manager + src/core/managed/Rtti src/core/model/Document src/core/model/Domain src/core/model/Typesystem @@ -184,8 +190,10 @@ IF(TEST) test/core/managed/ManagedContainerTest test/core/managed/ManagedTest test/core/managed/ManagerTest + test/core/managed/RttiTest test/core/model/DomainTest test/core/model/DocumentTest + test/core/model/TypesystemTest test/core/parser/ParserStackTest # test/core/script/FunctionTest # test/core/script/ObjectTest diff --git a/lib/download_dependencies.sh b/lib/download_dependencies.sh index 369c514..8217dd7 100755 --- a/lib/download_dependencies.sh +++ b/lib/download_dependencies.sh @@ -1,6 +1,12 @@ #!/bin/sh # Download and unzip googletest -wget https://googletest.googlecode.com/files/gtest-1.7.0.zip -unzip gtest-1.7.0.zip +if [ ! -e gtest-1.7.0.zip ]; then + wget https://googletest.googlecode.com/files/gtest-1.7.0.zip + unzip gtest-1.7.0.zip +fi +# Download utf8-cpp (header only library) +if [ ! -e utf8cpp ]; then + svn checkout svn://svn.code.sf.net/p/utfcpp/code/v2_0/source utf8cpp +fi diff --git a/lib/utf8cpp/utf8.h b/lib/utf8cpp/utf8.h new file mode 100644 index 0000000..4e44514 --- /dev/null +++ b/lib/utf8cpp/utf8.h @@ -0,0 +1,34 @@ +// Copyright 2006 Nemanja Trifunovic
+
+/*
+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.
+*/
+
+
+#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "utf8/checked.h"
+#include "utf8/unchecked.h"
+
+#endif // header guard
diff --git a/lib/utf8cpp/utf8/checked.h b/lib/utf8cpp/utf8/checked.h new file mode 100644 index 0000000..1331155 --- /dev/null +++ b/lib/utf8cpp/utf8/checked.h @@ -0,0 +1,327 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +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. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include <stdexcept> + +namespace utf8 +{ + // Base for the exceptions that may be thrown from the library + class exception : public ::std::exception { + }; + + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!utf8::internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + return result; + } + + template <typename octet_iterator, typename output_iterator> + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) { + case internal::UTF8_OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + out = utf8::append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } + } + return out; + } + + template <typename octet_iterator, typename output_iterator> + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::replace_invalid(start, end, out, replacement_marker); + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); + switch (err_code) { + case internal::UTF8_OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return utf8::next(it, end); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it, octet_iterator start) + { + // can't do much if it == start + if (it == start) + throw not_enough_room(); + + octet_iterator end = it; + // Go back until we hit either a lead octet or start + while (utf8::internal::is_trail(*(--it))) + if (it == start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + return utf8::peek_next(it, end); + } + + /// Deprecated in versions that include "prior" + template <typename octet_iterator> + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (utf8::internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return utf8::next(temp, end); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + utf8::next(it, end); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::next(first, last); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + if (utf8::internal::is_trail_surrogate(trail_surrogate)) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast<uint16_t>(trail_surrogate)); + } + else + throw invalid_utf16(static_cast<uint16_t>(cp)); + + } + // Lone trail surrogate + else if (utf8::internal::is_trail_surrogate(cp)) + throw invalid_utf16(static_cast<uint16_t>(cp)); + + result = utf8::append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = utf8::next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start != end) + (*result++) = utf8::next(start, end); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + utf8::next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + utf8::next(it, range_end); + return temp; + } + iterator& operator -- () + { + utf8::prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + diff --git a/lib/utf8cpp/utf8/core.h b/lib/utf8cpp/utf8/core.h new file mode 100644 index 0000000..693d388 --- /dev/null +++ b/lib/utf8cpp/utf8/core.h @@ -0,0 +1,329 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +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. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include <iterator> + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template<typename octet_type> + inline uint8_t mask8(octet_type oc) + { + return static_cast<uint8_t>(0xff & oc); + } + template<typename u16_type> + inline uint16_t mask16(u16_type oc) + { + return static_cast<uint16_t>(0xffff & oc); + } + template<typename octet_type> + inline bool is_trail(octet_type oc) + { + return ((utf8::internal::mask8(oc) >> 6) == 0x2); + } + + template <typename u16> + inline bool is_lead_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + } + + template <typename u16> + inline bool is_trail_surrogate(u16 cp) + { + return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template <typename u16> + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template <typename u32> + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); + } + + template <typename octet_iterator> + inline typename std::iterator_traits<octet_iterator>::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = utf8::internal::mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + template <typename octet_difference_type> + inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) + { + if (cp < 0x80) { + if (length != 1) + return true; + } + else if (cp < 0x800) { + if (length != 2) + return true; + } + else if (cp < 0x10000) { + if (length != 3) + return true; + } + + return false; + } + + enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + /// Helper for get_sequence_x + template <typename octet_iterator> + utf_error increase_safely(octet_iterator& it, octet_iterator end) + { + if (++it == end) + return NOT_ENOUGH_ROOM; + + if (!utf8::internal::is_trail(*it)) + return INCOMPLETE_SEQUENCE; + + return UTF8_OK; + } + + #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + + /// get_sequence_x functions decode utf-8 sequences of the length x + template <typename octet_iterator> + utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + + template <typename octet_iterator> + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + // Save the original value of it so we can go back in case of failure + // Of course, it does not make much sense with i.e. stream iterators + octet_iterator original_it = it; + + uint32_t cp = 0; + // Determine the sequence length based on the lead octet + typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type; + const octet_difference_type length = utf8::internal::sequence_length(it); + + // Get trail octets and calculate the code point + utf_error err = UTF8_OK; + switch (length) { + case 0: + return INVALID_LEAD; + case 1: + err = utf8::internal::get_sequence_1(it, end, cp); + break; + case 2: + err = utf8::internal::get_sequence_2(it, end, cp); + break; + case 3: + err = utf8::internal::get_sequence_3(it, end, cp); + break; + case 4: + err = utf8::internal::get_sequence_4(it, end, cp); + break; + } + + if (err == UTF8_OK) { + // Decoding succeeded. Now, security checks... + if (utf8::internal::is_code_point_valid(cp)) { + if (!utf8::internal::is_overlong_sequence(cp, length)){ + // Passed! Return here. + code_point = cp; + ++it; + return UTF8_OK; + } + else + err = OVERLONG_SEQUENCE; + } + else + err = INVALID_CODE_POINT; + } + + // Failure branch - restore the original value of the iterator + it = original_it; + return err; + } + + template <typename octet_iterator> + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + uint32_t ignored; + return utf8::internal::validate_next(it, end, ignored); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template <typename octet_iterator> + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); + if (err_code != internal::UTF8_OK) + return result; + } + return result; + } + + template <typename octet_iterator> + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (utf8::find_invalid(start, end) == end); + } + + template <typename octet_iterator> + inline bool starts_with_bom (octet_iterator it, octet_iterator end) + { + return ( + ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && + ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && + ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) + ); + } + + //Deprecated in release 2.3 + template <typename octet_iterator> + inline bool is_bom (octet_iterator it) + { + return ( + (utf8::internal::mask8(*it++)) == bom[0] && + (utf8::internal::mask8(*it++)) == bom[1] && + (utf8::internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + diff --git a/lib/utf8cpp/utf8/unchecked.h b/lib/utf8cpp/utf8/unchecked.h new file mode 100644 index 0000000..cb24271 --- /dev/null +++ b/lib/utf8cpp/utf8/unchecked.h @@ -0,0 +1,228 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +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. +*/ + + +#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" + +namespace utf8 +{ + namespace unchecked + { + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + return result; + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it) + { + uint32_t cp = utf8::internal::mask8(*it); + typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it); + switch (length) { + case 1: + break; + case 2: + it++; + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + break; + case 3: + ++it; + cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + ++it; + cp += (*it) & 0x3f; + break; + case 4: + ++it; + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + ++it; + cp += (utf8::internal::mask8(*it) << 6) & 0xfff; + ++it; + cp += (*it) & 0x3f; + break; + } + ++it; + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it) + { + return utf8::unchecked::next(it); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it) + { + while (utf8::internal::is_trail(*(--it))) ; + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + + // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) + template <typename octet_iterator> + inline uint32_t previous(octet_iterator& it) + { + return utf8::unchecked::prior(it); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n) + { + for (distance_type i = 0; i < n; ++i) + utf8::unchecked::next(it); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::unchecked::next(first); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + } + result = utf8::unchecked::append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start < end) { + uint32_t cp = utf8::unchecked::next(start); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::unchecked::append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = utf8::unchecked::next(start); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + bool operator == (const iterator& rhs) const + { + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + ::std::advance(it, utf8::internal::sequence_length(it)); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + ::std::advance(it, utf8::internal::sequence_length(it)); + return temp; + } + iterator& operator -- () + { + utf8::unchecked::prior(it); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::unchecked::prior(it); + return temp; + } + }; // class iterator + + } // namespace utf8::unchecked +} // namespace utf8 + + +#endif // header guard + diff --git a/src/core/common/Variant.cpp b/src/core/common/Variant.cpp index 6b99add..53286a2 100644 --- a/src/core/common/Variant.cpp +++ b/src/core/common/Variant.cpp @@ -132,10 +132,16 @@ Variant::stringType Variant::toString(bool escape) const return "null"; case Type::BOOL: return asBool() ? "true" : "false"; - case Type::INT: - return std::to_string(asInt()); - case Type::DOUBLE: - return std::to_string(asDouble()); + case Type::INT: { + std::stringstream ss; + ss << asInt(); + return ss.str(); + } + case Type::DOUBLE: { + std::stringstream ss; + ss << asDouble(); + return ss.str(); + } case Type::STRING: { // TODO: Use proper serialization function if (escape) { diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index cc25eac..ccc14f8 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -21,6 +21,8 @@ #include <cmath> #include <sstream> +#include <utf8.h> + #include "VariantReader.hpp" #include "Utils.hpp" @@ -79,11 +81,6 @@ static std::pair<bool, T> unexpected(CharReader &reader, Logger &logger, class Number { private: /** - * Reprsents the part of the number: Base value a, nominator n, exponent e. - */ - enum class Part { A, N, E }; - - /** * State used in the parser state machine */ enum class State { @@ -117,43 +114,12 @@ private: return -1; } +public: /** - * Appends the value of the character c to the internal number - * representation and reports any errors that might occur. + * Reprsents the part of the number: Base value a, nominator n, exponent e. */ - bool appendChar(char c, int base, Part p, CharReader &reader, - Logger &logger) - { - // Check whether the given character is valid - int v = charValue(c); - if (v < 0 || v >= base) { - logger.error(unexpectedMsg("digit", c), reader); - return false; - } - - // Append the number to the specified part - switch (p) { - case Part::A: - a = a * base + v; - break; - case Part::N: - n = n * base + v; - d = d * base; - break; - case Part::E: - e = e * base + v; - break; - } - - // Check for any overflows - if (a < 0 || n < 0 || d < 0 || e < 0) { - logger.error(ERR_TOO_LARGE, reader); - return false; - } - return true; - } + enum class Part { A, N, E }; -public: /** * Sign and exponent sign. */ @@ -196,12 +162,51 @@ public: bool isInt() { return (n == 0) && (d == 1) && (e == 0); } /** + * Appends the value of the character c to the internal number + * representation and reports any errors that might occur. + */ + bool appendChar(char c, int base, Part p, CharReader &reader, + Logger &logger) + { + // Check whether the given character is valid + int v = charValue(c); + if (v < 0 || v >= base) { + logger.error(unexpectedMsg("digit", c), reader); + return false; + } + + // Append the number to the specified part + switch (p) { + case Part::A: + a = a * base + v; + break; + case Part::N: + n = n * base + v; + d = d * base; + break; + case Part::E: + e = e * base + v; + break; + } + + // Check for any overflows + if (a < 0 || n < 0 || d < 0 || e < 0) { + logger.error(ERR_TOO_LARGE, reader); + return false; + } + return true; + } + + /** * Tries to parse the number from the given stream and loggs any errors to * the given logger instance. Numbers are terminated by one of the given * delimiters. */ bool parse(CharReader &reader, Logger &logger, const std::unordered_set<char> &delims); + + bool parseFixedLenInt(CharReader &reader, Logger &logger, int base, + int len); }; bool Number::parse(CharReader &reader, Logger &logger, @@ -332,6 +337,24 @@ bool Number::parse(CharReader &reader, Logger &logger, return false; } +bool Number::parseFixedLenInt(CharReader &reader, Logger &logger, int base, + int len) +{ + char c; + reader.consumePeek(); + for (int i = 0; i < len; i++) { + if (!reader.peek(c)) { + logger.error("Unexpected end of escape sequence", reader); + return false; + } + if (!appendChar(c, base, Number::Part::A, reader, logger)) { + return false; + } + reader.consumePeek(); + } + return true; +} + /* State machine states */ static const int STATE_INIT = 0; @@ -535,10 +558,32 @@ static std::pair<bool, Variant> parseComplex(CharReader &reader, Logger &logger, /* Class Reader */ +static bool encodeUtf8(std::stringstream &res, CharReader &reader, + Logger &logger, int64_t v, bool latin1) +{ + // Encode the unicode codepoint as UTF-8 + uint32_t cp = static_cast<uint32_t>(v); + if (latin1 && cp > 0xFF) { + logger.error("Not a valid ISO-8859-1 (Latin-1) character, skipping", reader); + return false; + } + + // Append the code point to the output stream + try { + utf8::append(cp, std::ostream_iterator<uint8_t>{res}); + return true; + } + catch (utf8::invalid_code_point ex) { + logger.error("Invalid Unicode codepoint, skipping", reader); + } + return false; +} + std::pair<bool, std::string> VariantReader::parseString( CharReader &reader, Logger &logger, const std::unordered_set<char> *delims) { // Initialize the internal state + bool hadError = false; int state = STATE_INIT; char quote = 0; std::stringstream res; @@ -565,7 +610,7 @@ std::pair<bool, std::string> VariantReader::parseString( case STATE_IN_STRING: if (c == quote) { reader.consumePeek(); - return std::make_pair(true, res.str()); + return std::make_pair(!hadError, res.str()); } else if (c == '\\') { state = STATE_ESCAPE; reader.consumePeek(); @@ -608,17 +653,39 @@ std::pair<bool, std::string> VariantReader::parseString( break; case '\n': break; - case 'x': - // TODO: Parse Latin-1 sequence hex XX + case 'x': { + // Parse Latin-1 sequence \xXX + Number n; + hadError = + !(n.parseFixedLenInt(reader, logger, 16, 2) && + encodeUtf8(res, reader, logger, n.intValue(), + true)) || + hadError; break; - case 'u': - // TODO: Parse 16-Bit unicode character hex XXXX + } + case 'u': { + // Parse Unicode sequence \uXXXX + Number n; + hadError = + !(n.parseFixedLenInt(reader, logger, 16, 4) && + encodeUtf8(res, reader, logger, n.intValue(), + false)) || + hadError; break; + } default: if (Utils::isNumeric(c)) { - // TODO: Parse octal 000 sequence + // Parse Latin-1 sequence \000 + reader.resetPeek(); + Number n; + hadError = + !(n.parseFixedLenInt(reader, logger, 8, 3) && + encodeUtf8(res, reader, logger, n.intValue(), + true)) || + hadError; } else { logger.error(ERR_INVALID_ESCAPE, reader); + hadError = true; } break; } diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp index 4818c3d..8582702 100644 --- a/src/core/managed/Managed.hpp +++ b/src/core/managed/Managed.hpp @@ -19,7 +19,7 @@ #ifndef _OUSIA_MANAGED_HPP_ #define _OUSIA_MANAGED_HPP_ -#include "ManagedType.hpp" +#include "Rtti.hpp" #include "Manager.hpp" namespace ousia { @@ -108,25 +108,25 @@ public: bool deleteData(const std::string &key); /** - * Returns the ManagedType instance registered for instances of the type - * of this Managed instance. + * Returns the RttiBase instance registered for instances of the type of + * this Managed instance. * - * @return a reference to the registered ManagedType for this particular + * @return a reference to the registered RttiBase for this particular * Managed class. */ - const ManagedType& type() const { - return ManagedType::typeOf(typeid(*this)); + const RttiBase &type() const + { + return typeOf(*this); } /** - * Returns true if this Managed instance is of the given ManagedType. + * Returns true if this Managed instance is of the given RttiBase. * - * @param true if the ManagedType registered for this particular Managed - * class is + * @param true if the RttiBase registered for this particular Managed + * class is of the given type or one of the registered parent types is of + * the given type. */ - bool isa(const ManagedType &t) const { - return type().isa(t); - } + bool isa(const RttiBase &t) const { return type().isa(t); } }; /** @@ -512,7 +512,6 @@ public: */ Managed *getOwner() const { return owner; } }; - } #endif /* _OUSIA_MANAGED_HPP_ */ diff --git a/src/core/managed/ManagedType.hpp b/src/core/managed/ManagedType.hpp deleted file mode 100644 index f3ed5fd..0000000 --- a/src/core/managed/ManagedType.hpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef _OUSIA_MANAGED_TYPE_HPP_ -#define _OUSIA_MANAGED_TYPE_HPP_ - -#include <typeinfo> -#include <typeindex> -#include <unordered_map> -#include <unordered_set> - -namespace ousia { - -/** - * The ManagedType is used to register type information that can be retrieved - * using the "type" method of the Managed class. - */ -class ManagedType { -private: - /** - * Used internally to store all registered native types and their - * corresponding type information. - */ - static std::unordered_map<std::type_index, ManagedType *>& table() { - static std::unordered_map<std::type_index, ManagedType *> table; - return table; - } - - /** - * Name of the type -- for messages and debug output. - */ - const std::string name; - - /** - * Set containing references to the parent types. - */ - const std::unordered_set<ManagedType *> parents; - -public: - /** - * ManagedType of no particular type. - */ - static const ManagedType None; - - /** - * Returns the ManagedType for the given type_info structure. - */ - static const ManagedType &typeOf(const std::type_info &nativeType); - - /** - * Default constructor. Creates a ManagedType instance with name "unknown" - * and no parents. - */ - ManagedType() : name("unknown") {} - - /** - * Creates a new ManagedType instance and registers it in the global type - * table. - * - * @param name is the name of the type. - * @param nativeType is the underlying C++ class the type should be attached - * to. - */ - ManagedType(std::string name, const std::type_info &nativeType) - : name(std::move(name)) - { - table().emplace(std::make_pair(std::type_index{nativeType}, this)); - } - - /** - * Creates a new ManagedType instance and registers it in the global type - * table. - * - * @param name is the name of the type. - * @param nativeType is the underlying C++ class the type should be attached - * to. - * @param parents is a list of parent types. - */ - ManagedType(std::string name, const std::type_info &nativeType, - std::unordered_set<ManagedType *> parents) - : name(std::move(name)), parents(parents) - { - table().emplace(std::make_pair(std::type_index{nativeType}, this)); - } - - /** - * Returns the name of this type. - */ - std::string getName() const { return name; } - - /** - * Returns true if this ManagedType instance is the given type or has the - *given - * type as one of its parents. - * - * @param other is the other type for which the relation to this type - * should be checked. - */ - bool isa(const ManagedType &other) const; -}; -} - -#endif /* _OUSIA_MANAGED_TYPE_HPP_ */ - diff --git a/src/core/managed/ManagedType.cpp b/src/core/managed/Rtti.cpp index ed4c7da..eade524 100644 --- a/src/core/managed/ManagedType.cpp +++ b/src/core/managed/Rtti.cpp @@ -16,27 +16,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ManagedType.hpp" +#include "Rtti.hpp" namespace ousia { -/* Instantiation of static variables */ +/* Class RttiStore */ -const ManagedType ManagedType::None; +std::unordered_map<std::type_index, const RttiBase *> &RttiStore::table() +{ + static std::unordered_map<std::type_index, const RttiBase *> table; + return table; +} -/* Class ManagedType */ +void RttiStore::store(const std::type_info &native, const RttiBase *rtti) +{ + table().emplace(std::type_index{native}, rtti); +} -const ManagedType &ManagedType::typeOf(const std::type_info &nativeType) +const RttiBase &RttiStore::lookup(const std::type_info &native) { - auto it = table().find(std::type_index{nativeType}); - if (it == table().end()) { - return None; + const auto &tbl = table(); + auto it = tbl.find(std::type_index{native}); + if (it == tbl.end()) { + return RttiBase::None; } else { return *(it->second); } } -bool ManagedType::isa(const ManagedType &other) const +/* Class RttiBase */ + +const RttiBase RttiBase::None; + +bool RttiBase::isa(const RttiBase &other) const { if (&other == this) { return true; @@ -48,5 +60,7 @@ bool ManagedType::isa(const ManagedType &other) const } return false; } + + } diff --git a/src/core/managed/Rtti.hpp b/src/core/managed/Rtti.hpp new file mode 100644 index 0000000..f53fd9b --- /dev/null +++ b/src/core/managed/Rtti.hpp @@ -0,0 +1,183 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file Rtti.hpp + * + * Classes used for storing runtime type information (RTTI). RTTI is used to + * lookup objects in the object graph of a certain type and to attach + * information that should be accessible to the script engine. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_MANAGED_RTTI_HPP_ +#define _OUSIA_MANAGED_RTTI_HPP_ + +#include <typeinfo> +#include <typeindex> +#include <unordered_map> +#include <vector> + +namespace ousia { + +class RttiBase; + +/** + * Helper class used to globally store and access the runtime type information. + */ +class RttiStore { +private: + /** + * Function used internally to access the static map storing all registered + * native types and their corresponding type information. + */ + static std::unordered_map<std::type_index, const RttiBase *> &table(); + +public: + /** + * Registers the given pointer to the RttiBase class in the RTTI table. Does + * not override information for already registered types. + * + * @param native is a reference at the native type information provided + * by the compiler. + * @param rtti is a pointer pointing at the type information that should be + * stored for this type. + */ + static void store(const std::type_info &native, const RttiBase *rtti); + + /** + * Looks up the type information stored for the given native type + * information. + */ + static const RttiBase &lookup(const std::type_info &native); +}; + +/** + * The Rtti class allows for attaching data to native types that can be accessed + * at runtime. This type information can e.g. be retrieved using the "type" + * method of the Managed class. This system is used for attaching human readable + * names, parent types and script engine functionality. Use the Rtti class for + * convenient registration of type information. + */ +class RttiBase { +private: + /** + * Set containing references to the parent types. + */ + const std::vector<const RttiBase *> parents; + +public: + /** + * Rtti of no particular type. + */ + static const RttiBase None; + + /** + * Human readable name associated with the type. + */ + const std::string name; + + /** + * Default constructor. Creates a Rtti instance with name "unknown" + * and no parents. + */ + RttiBase() : name("unknown") {} + + /** + * Creates a new RttiBase instance and registers it in the global type + * table. Use the Rtti class for more convinient registration of type + * information. + * + * @param name is the name of the type. + * @param native is a reference at the native type information provided by + * the compiler. + * @param parents is a list of parent types. + */ + RttiBase(std::string name, const std::type_info &native, + std::vector<const RttiBase *> parents = + std::vector<const RttiBase *>{}) + : parents(std::move(parents)), name(std::move(name)) + { + RttiStore::store(native, this); + } + + /** + * Returns true if this Rtti instance is the given type or has the + * given type as one of its parents. + * + * @param other is the other type for which the relation to this type + * should be checked. + */ + bool isa(const RttiBase &other) const; +}; + +/** + * The Rtti class allows for attaching data to native types that can be accessed + * at runtime. This type information can e.g. be retrieved using the "type" + * method of the Managed class. This system is used for attaching human + * readable names, parent types and script engine functionality. + * + * @tparam T is the class for which the type information should be registered. + */ +template <class T> +class Rtti : public RttiBase { +public: + /** + * Creates a new RttiBase instance and registers it in the global type + * table. + * + * @param name is the name of the type. + * @param parents is a list of parent types. + */ + Rtti(std::string name, const std::vector<const RttiBase *> &parents = + std::vector<const RttiBase *>{}) + : RttiBase(name, typeid(T), parents) + { + } +}; + +/** + * Function that can be used to retrieve the RTTI information of a Managed + * object. + * + * @tparam T is the C++ type for which the type information should be returned. + */ +template <typename T> +inline const RttiBase &typeOf() +{ + return RttiStore::lookup(typeid(T)); +} + +/** + * Function that can be used to retrieve the RTTI information of a Managed + * object. + * + * @tparam T is the C++ type for which the type information should be returned. + * @param obj is a dummy object for which the type information should be + * returned. + */ +template <typename T> +inline const RttiBase &typeOf(const T &obj) +{ + return RttiStore::lookup(typeid(obj)); +} +} + +#endif /* _OUSIA_MANAGED_RTTI_HPP_ */ + diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp index f3c49dc..724bf0e 100644 --- a/src/core/model/Typesystem.cpp +++ b/src/core/model/Typesystem.cpp @@ -23,9 +23,42 @@ namespace ousia { namespace model { -EnumerationType EnumerationType::createValidated( - Manager &mgr, std::string name, Handle<Typesystem> system, - const std::vector<std::string> &values, Logger &logger) +/* Class Type */ + +bool Type::build(Variant &var, Logger &logger) const +{ + try { + return doBuild(var, logger); + } + catch (LoggableException ex) { + logger.log(ex); + var = create(); + return false; + } +} + +/* Class StringType */ + +bool StringType::doBuild(Variant &var, Logger &logger) const +{ + if (!var.isPrimitive()) { + throw LoggableException{"Expected a string or primitive input."}; + } + + if (!var.isString()) { + logger.note(std::string("Implicit type conversion from ") + + var.getTypeName() + " to string."); + } + var = Variant{var.toString().c_str()}; + return true; +} + +/* Class EnumType */ + +EnumType EnumType::createValidated(Manager &mgr, std::string name, + Handle<Typesystem> system, + const std::vector<std::string> &values, + Logger &logger) { std::map<std::string, size_t> unique_values; for (size_t i = 0; i < values.size(); i++) { @@ -38,8 +71,18 @@ EnumerationType EnumerationType::createValidated( " was duplicated."); } } - return std::move(EnumerationType(mgr, name, system, unique_values)); + return std::move(EnumType(mgr, name, system, unique_values)); } + +/* RTTI type registrations */ + +const Rtti<Type> Type_T("Type"); +const Rtti<StringType> StringType_T("StringType", {&Type_T}); +const Rtti<IntType> IntType_T("IntType", {&Type_T}); +const Rtti<DoubleType> DoubleType_T("DoubleType", {&Type_T}); +const Rtti<BoolType> BoolType_T("BoolType", {&Type_T}); +const Rtti<EnumType> EnumType_T("EnumType", {&Type_T}); +const Rtti<StructType> StructType_T("StructType", {&Type_T}); } } diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 20c6e8b..c9793e2 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -19,7 +19,9 @@ /** * @file Typesystem.hpp * - * TODO: Docu + * Contains the Entities described in a Typesystem. A Typesystem is a list + * of type descriptors, where a type is either primitive or a user defined + * type. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) */ @@ -40,79 +42,103 @@ namespace model { class Typesystem; +/** + * The abstract Type class represents a type descriptor. Each Type node is part + * of a Typesystem instance. Concrete instances of the Type class are immutable + * (they are guaranteed to represent exactly one type). Note that Type classes + * only contain the type description, instances of the Type class do not hold + * any data. Data is held by instances of the Variant class. How exactly the + * data is represented within the Variant instances is defined by the type + * definitions. + */ class Type : public Node { protected: + /** + * Protected constructor to be called by the classes derived from the Type + * class. + * + * @param mgr is the Manager instance to be used for the Node. + * @param name is the name of the type. + * @param system is a reference to the parent TypeSystem instance. + * @param primitive is set to true for primitive types, such as ints, + * doubles, strings and enums. + */ Type(Manager &mgr, std::string name, Handle<Typesystem> system, - bool inheritable, bool primitive) - : Node(mgr, std::move(name), system), - inheritable(inheritable), - primitive(primitive) + bool primitive) + : Node(mgr, std::move(name), system), primitive(primitive) { } - virtual bool doPrepare(Variant &var, Logger &log) const = 0; - -public: /** - * TODO: DOC + * Validates and completes the given variant. This pure virtual doBuild + * method must be overridden by derived classes. This function may throw + * an LoggableException in case the given data cannot be converted to + * the internal representation given by the type descriptor. + * + * @param var is a variant containing the data that should be checked and + * -- if possible and necessary -- converted to a variant adhering to the + * internal representation used by the Type class. + * @param logger is the Logger instance into which errors should be written. + * @return true if the conversion was successful, false otherwise. */ - const bool inheritable; + virtual bool doBuild(Variant &var, Logger &logger) const = 0; + +public: /** - * TODO: DOC + * Set to true, if this type descriptor is a primitive type. */ const bool primitive; /** - * TODO: DOC + * Pure virtual function which must construct a valid, default instance of + * the type that is being described by the typesystem. */ virtual Variant create() const = 0; /** - * TODO: DOC + * Validates and completes the given variant which was read from a + * user-supplied source. + * + * @param var is a variant containing the data that should be checked and + * -- if possible and necessary -- converted to a variant adhering to the + * internal representation used by the Type class. + * @param logger is the Logger instance into which errors should be written. + * @return true if the conversion was successful, false otherwise. */ - bool prepare(Variant &var, Logger &log) const - { - try { - return doPrepare(var, log); - } - catch (LoggableException ex) { - log.log(ex); - var = create(); - return false; - } - } + bool build(Variant &var, Logger &logger) const; }; +/** + * The StringType class represents the primitive string type. There should + * exactly be a single instance of this class available in a preloaded type + * system. + */ class StringType : public Type { protected: /** - * TODO: DOC + * If possible, converts the given variant to a string. Only works, if the + * variant contains primitive objects (integers, strings, booleans, etc.). */ - bool doPrepare(Variant &var, Logger &log) const override - { - if (!var.isPrimitive()) { - throw LoggableException{"Expected a string or primitive input."}; - } - - if (!var.isString()) { - log.note(std::string("Implicit type conversion from ") + - var.getTypeName() + " to string."); - } - var = Variant{var.toString().c_str()}; - return true; - } + bool doBuild(Variant &var, Logger &logger) const override; public: /** - * TODO: DOC + * Constructor of the StringType class. Only one instance of StringType + * should exist per project. + * + * @param mgr is the Manager instance to be used for the Node. + * @param name is the name of the type. + * @param system is a reference to the parent TypeSystem instance. */ StringType(Manager &mgr, Handle<Typesystem> system) - : Type(mgr, "string", system, false, true) + : Type(mgr, "string", system, true) { } /** - * TODO: DOC + * Creates a variant containing an empty string. + * + * @return a variant containing an empty string. */ Variant create() const override { return Variant{""}; } }; @@ -122,7 +148,7 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { if (!var.isInt()) { throw LoggableException{"Expected an integer value."}; @@ -135,7 +161,7 @@ public: * TODO: DOC */ IntType(Manager &mgr, Handle<Typesystem> system) - : Type(mgr, "int", system, false, true) + : Type(mgr, "int", system, true) { } @@ -150,7 +176,7 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { if (!var.isInt() && !var.isDouble()) { throw LoggableException{"Expected a double value."}; @@ -164,7 +190,7 @@ public: * TODO: DOC */ DoubleType(Manager &mgr, Handle<Typesystem> system) - : Type(mgr, "double", system, false, true) + : Type(mgr, "double", system, true) { } @@ -179,14 +205,14 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override { return true; } + bool doBuild(Variant &var, Logger &logger) const override { return true; } public: /** * TODO: DOC */ UnknownType(Manager &mgr, Handle<Typesystem> system) - : Type(mgr, "unknown", system, false, true) + : Type(mgr, "unknown", system, true) { } @@ -201,7 +227,7 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { if (!var.isBool()) { throw LoggableException("Expected boolean value!"); @@ -214,7 +240,7 @@ public: * TODO: DOC */ BoolType(Manager &mgr, Handle<Typesystem> system) - : Type(mgr, "bool", system, false, true) + : Type(mgr, "bool", system, true) { } @@ -224,7 +250,7 @@ public: Variant create() const override { return Variant{false}; } }; -class EnumerationType : public Type { +class EnumType : public Type { private: std::map<std::string, size_t> values; @@ -232,7 +258,7 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { if (var.isInt()) { int i = var.asInt(); @@ -245,10 +271,9 @@ protected: return true; } - EnumerationType(Manager &mgr, std::string name, Handle<Typesystem> system, - std::map<std::string, size_t> values) - : Type(mgr, std::move(name), system, false, false), - values(std::move(values)) + EnumType(Manager &mgr, std::string name, Handle<Typesystem> system, + std::map<std::string, size_t> values) + : Type(mgr, std::move(name), system, false), values(std::move(values)) { } @@ -256,9 +281,9 @@ public: /** * TODO: DOC */ - EnumerationType(Manager &mgr, std::string name, Handle<Typesystem> system, - const std::vector<std::string> &values) - : Type(mgr, std::move(name), system, false, false) + EnumType(Manager &mgr, std::string name, Handle<Typesystem> system, + const std::vector<std::string> &values) + : Type(mgr, std::move(name), system, false) { for (size_t i = 0; i < values.size(); i++) { this->values.insert(std::make_pair(values[i], i)); @@ -268,9 +293,10 @@ public: /** * TODO: DOC */ - static EnumerationType createValidated( - Manager &mgr, std::string name, Handle<Typesystem> system, - const std::vector<std::string> &values, Logger &logger); + static EnumType createValidated(Manager &mgr, std::string name, + Handle<Typesystem> system, + const std::vector<std::string> &values, + Logger &logger); /** * TODO: DOC @@ -300,13 +326,13 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { // If we already have an array, we just check that. - if(var.isArray()){ + if (var.isArray()) { auto arr = var.asArray(); - for(size_t a = 0; a < attrs.size(); a++){ - if(!attrs[a].type->prepare(arr[a], log)){ + for (size_t a = 0; a < attrs.size(); a++) { + if (!attrs[a].type->build(arr[a], logger)) { return false; } } @@ -323,10 +349,10 @@ protected: for (auto &a : attrs) { auto it = map.find(a.name); // we use the default if nothing is set. - if (it == map.end() || !a.type->prepare(it->second, log)) { - log.note(std::string("Using default value for ") + a.name); + if (it == map.end() || !a.type->build(it->second, logger)) { + logger.note(std::string("Using default value for ") + a.name); vec.push_back(a.defaultValue); - } else{ + } else { vec.push_back(it->second); } } @@ -339,8 +365,7 @@ public: StructType(Manager &mgr, std::string name, Handle<Typesystem> system, std::vector<AttributeDescriptor> attrs) - : Type(mgr, std::move(name), system, true, false), - attrs(std::move(attrs)) + : Type(mgr, std::move(name), system, false), attrs(std::move(attrs)) { } // TODO @@ -360,14 +385,14 @@ protected: /** * TODO: DOC */ - bool doPrepare(Variant &var, Logger &log) const override + bool doBuild(Variant &var, Logger &logger) const override { if (!var.isArray()) { throw LoggableException("Expected array!"); } bool res = true; for (auto &v : var.asArray()) { - if (!innerType->prepare(v, log)) { + if (!innerType->build(v, logger)) { res = false; } } @@ -381,7 +406,7 @@ public: */ ArrayType(Manager &mgr, std::string name, Handle<Typesystem> system, Handle<Type> innerType) - : Type(mgr, std::move(name), system, false, false), + : Type(mgr, std::move(name), system, false), innerType(acquire(innerType)) { } diff --git a/test/core/common/VariantReaderTest.cpp b/test/core/common/VariantReaderTest.cpp index 7972374..3d4e7bd 100644 --- a/test/core/common/VariantReaderTest.cpp +++ b/test/core/common/VariantReaderTest.cpp @@ -60,6 +60,71 @@ TEST(VariantReader, readString) ASSERT_TRUE(res.first); ASSERT_EQ("'\"\b\f\n\r\t\v", res.second); } + + + // Hex Unicode character + { + CharReader reader("'linebreak\\u000A in unicode'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("linebreak\n in unicode", res.second); + } +} + +TEST(VariantReader, readStringUnicode) +{ + // Hex Unicode character + { + CharReader reader("'linebreak \\u000A in unicode'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("linebreak \n in unicode", res.second); + } + + // Hex Unicode character + { + CharReader reader("'hammer and sickle \\u262D in unicode'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("hammer and sickle \342\230\255 in unicode", res.second); + } + + // Octal Latin-1 character + { + CharReader reader("'copyright symbol \\251 in Unicode'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("copyright symbol \302\251 in Unicode", res.second); + } + + // Hexadecimal Latin-1 character + { + CharReader reader("'copyright symbol \\xA9 in Unicode'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("copyright symbol \302\251 in Unicode", res.second); + } + + // Errornous unicode escape sequence + { + CharReader reader("'\\uBLUB'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_FALSE(res.first); + } + + // Errornous octal escape sequence + { + CharReader reader("'\\400'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_FALSE(res.first); + } + + // Errornous hexadecimal latin1 escape sequence + { + CharReader reader("'\\xa'"); + auto res = VariantReader::parseString(reader, logger, {';'}); + ASSERT_FALSE(res.first); + } } TEST(VariantReader, parseUnescapedString) diff --git a/test/core/managed/ManagedTest.cpp b/test/core/managed/ManagedTest.cpp index f196770..c88cf7a 100644 --- a/test/core/managed/ManagedTest.cpp +++ b/test/core/managed/ManagedTest.cpp @@ -75,33 +75,10 @@ class TypeTestManaged5 : public Managed { using Managed::Managed; }; -ManagedType Type1("Type1", typeid(TypeTestManaged1)); -ManagedType Type2("Type2", typeid(TypeTestManaged2)); -ManagedType Type3("Type3", typeid(TypeTestManaged3), {&Type1}); -ManagedType Type4("Type2", typeid(TypeTestManaged4), {&Type3, &Type2}); - -TEST(ManagedType, isa) -{ - ASSERT_TRUE(Type1.isa(Type1)); - ASSERT_FALSE(Type1.isa(Type2)); - ASSERT_FALSE(Type1.isa(Type3)); - ASSERT_FALSE(Type1.isa(Type4)); - - ASSERT_FALSE(Type2.isa(Type1)); - ASSERT_TRUE(Type2.isa(Type2)); - ASSERT_FALSE(Type2.isa(Type3)); - ASSERT_FALSE(Type2.isa(Type4)); - - ASSERT_TRUE(Type3.isa(Type1)); - ASSERT_FALSE(Type3.isa(Type2)); - ASSERT_TRUE(Type3.isa(Type3)); - ASSERT_FALSE(Type3.isa(Type4)); - - ASSERT_TRUE(Type4.isa(Type1)); - ASSERT_TRUE(Type4.isa(Type2)); - ASSERT_TRUE(Type4.isa(Type3)); - ASSERT_TRUE(Type4.isa(Type4)); -} +static const Rtti<TypeTestManaged1> Type1("Type1"); +static const Rtti<TypeTestManaged2> Type2("Type2"); +static const Rtti<TypeTestManaged3> Type3("Type3", {&Type1}); +static const Rtti<TypeTestManaged4> Type4("Type4", {&Type3, &Type2}); TEST(Managed, type) { @@ -117,7 +94,10 @@ TEST(Managed, type) ASSERT_EQ(&Type2, &m2->type()); ASSERT_EQ(&Type3, &m3->type()); ASSERT_EQ(&Type4, &m4->type()); - ASSERT_EQ(&ManagedType::None, &m5->type()); + ASSERT_EQ(&RttiBase::None, &m5->type()); + + ASSERT_EQ(&Type1, &typeOf<TypeTestManaged1>()); + ASSERT_EQ(&Type1, &typeOf(*m1)); } } diff --git a/test/core/managed/RttiTest.cpp b/test/core/managed/RttiTest.cpp new file mode 100644 index 0000000..091bdea --- /dev/null +++ b/test/core/managed/RttiTest.cpp @@ -0,0 +1,63 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <array> +#include <string> +#include <iostream> + +#include <gtest/gtest.h> + +#include <core/managed/Rtti.hpp> + +namespace ousia { + +class RttiTestClass1 {}; +class RttiTestClass2 {}; +class RttiTestClass3 {}; +class RttiTestClass4 {}; + +static const Rtti<RttiTestClass1> Type1("Type1"); +static const Rtti<RttiTestClass2> Type2("Type2"); +static const Rtti<RttiTestClass3> Type3("Type3", {&Type1}); +static const Rtti<RttiTestClass4> Type4("Type4", {&Type3, &Type2}); + +TEST(Rtti, isa) +{ + ASSERT_TRUE(Type1.isa(Type1)); + ASSERT_FALSE(Type1.isa(Type2)); + ASSERT_FALSE(Type1.isa(Type3)); + ASSERT_FALSE(Type1.isa(Type4)); + + ASSERT_FALSE(Type2.isa(Type1)); + ASSERT_TRUE(Type2.isa(Type2)); + ASSERT_FALSE(Type2.isa(Type3)); + ASSERT_FALSE(Type2.isa(Type4)); + + ASSERT_TRUE(Type3.isa(Type1)); + ASSERT_FALSE(Type3.isa(Type2)); + ASSERT_TRUE(Type3.isa(Type3)); + ASSERT_FALSE(Type3.isa(Type4)); + + ASSERT_TRUE(Type4.isa(Type1)); + ASSERT_TRUE(Type4.isa(Type2)); + ASSERT_TRUE(Type4.isa(Type3)); + ASSERT_TRUE(Type4.isa(Type4)); +} + +} + diff --git a/test/core/model/TypesystemTest.cpp b/test/core/model/TypesystemTest.cpp new file mode 100644 index 0000000..9939adf --- /dev/null +++ b/test/core/model/TypesystemTest.cpp @@ -0,0 +1,104 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gtest/gtest.h> + +#include <iostream> + +#include <core/model/Typesystem.hpp> + +namespace ousia { +namespace model { + +TEST(Type, rtti) +{ + Manager mgr(1); + Rooted<StringType> strType{new StringType(mgr, nullptr)}; + + ASSERT_TRUE(typeOf(*strType).isa(typeOf<Type>())); +} + +TEST(StringType, creation) +{ + Manager mgr(1); + Rooted<StringType> strType{new StringType(mgr, nullptr)}; + + Variant val = strType->create(); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("", val.asString()); +} + +TEST(StringType, conversion) +{ + Logger logger; + Manager mgr(1); + Rooted<StringType> strType{new StringType(mgr, nullptr)}; + + { + Variant val{42}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("42", val.asString()); + } + + { + Variant val{42.5}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("42.5", val.asString()); + } + + { + Variant val{true}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("true", val.asString()); + } + + { + Variant val{false}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("false", val.asString()); + } + + { + Variant val{nullptr}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("null", val.asString()); + } + + { + Variant val{"test"}; + ASSERT_TRUE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("test", val.asString()); + } + + { + Variant val{{1, 2, true, false}}; + ASSERT_FALSE(strType->build(val, logger)); + ASSERT_TRUE(val.isString()); + ASSERT_EQ("", val.asString()); + } + +} + +} +} |