/* * This file is a part of PikoTools * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name Tomasz Sowa nor the names of contributors to this * project may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef headerfile_picotools_space_space #define headerfile_picotools_space_space #include #include #include #include #include #include "textstream/types.h" #include "convert/inttostr.h" //#include "utf8/utf8.h" namespace PT { /* Simple form: key = value If value is equal to 'null' then the internal representation is equal to Type::type_null, e.g: key = null If value is either 'false' or 'true' then the internal representation is Type::type_bool, e.g: key = false key = true If value is an integer number and not an overflow has occured while parsing then type of the value is Type::type_long (type is 'long long' internally): key = 1000 If value is a floating point number then type is Type::type_double (type is 'double' internally): key = 123.45 key = 123.45e+10 In other cases value has Type::type_wstring type. The form with quotation marks: key = "value" In such a case value has Type::type_wstring type and can be multiline e.g: key = "multiline value" In this form you can have spaces around value, e.g.: key = " value with spaces " The value can be a list: key = (value1,value2) or the same written with spaces around: key = ( value1 , value2 ) or written in multiline fashion: key = ( value1, value2, ) a colon after value2 is optional. List can be written with quotation marks too: key = ( "value1", "value2", "value3 with a new line character inside", ) List without value: key = () is equal to simple form without value, e.g.: key = Difference between a simple form and a list is when parsing the colon, in simple form the colon is a part of the value, e.g.: key = value with , a colon inside this is equal to: key = "value with , a colon inside" and in a form of a list it would have two values: key = (value with , a colon inside) would be equal to: key = ("value with" , "a colon inside") */ class Space { public: typedef std::map ObjectType; typedef std::vector TableType; enum Escape { no_escape, escape_space, escape_json, }; enum Type { type_null, type_bool, type_long, type_float, type_double, type_string, type_wstring, type_object, type_table, }; union Value { bool value_bool; long long value_long; float value_float; double value_double; std::string value_string; std::wstring value_wstring; ObjectType value_object; TableType value_table; Value() { } ~Value() { } }; Type type; Value value; std::wstring * name; TableType * child_spaces; Space(); Space(const Space & space); Space & operator=(const Space & space); // add move cctor ~Space(); Space(bool val); Space(short val); Space(int val); Space(long val); Space(long long val); Space(unsigned short val); Space(unsigned int val); Space(unsigned long val); Space(unsigned long long val); Space(float val); Space(double val); Space(const char * str); Space(const wchar_t * str); Space(const std::string & str); Space(const std::wstring & str); Space(const Space * space); void clear(); // set a new value void set_null(); void set_empty_string(); void set_empty_wstring(); void set_empty_table(); void set_empty_object(); void set(bool val); void set(short val); void set(int val); void set(long val); void set(long long val); void set(unsigned short val); void set(unsigned int val); void set(unsigned long val); void set(unsigned long long val); void set(float val); void set(double val); void set(const char * str); void set(const wchar_t * str); void set(const std::string & str); void set(const std::wstring & str); void set(const Space * space); // add a value to the table, change to table if needed, return the reference to the new inserted item Space & add(bool val); Space & add(short val); Space & add(int val); Space & add(long val); Space & add(long long val); Space & add(unsigned short val); Space & add(unsigned int val); Space & add(unsigned long val); Space & add(unsigned long long val); Space & add(float val); Space & add(double val); Space & add(const char * val); Space & add(const wchar_t * val); Space & add(const std::string & val); Space & add(const std::wstring & val); Space & add(const Space * space); // add a value to the object, change to object if needed, return the reference to the new inserted item Space & add(const wchar_t * field, bool val); Space & add(const wchar_t * field, short val); Space & add(const wchar_t * field, int val); Space & add(const wchar_t * field, long val); Space & add(const wchar_t * field, long long val); Space & add(const wchar_t * field, unsigned short val); Space & add(const wchar_t * field, unsigned int val); Space & add(const wchar_t * field, unsigned long val); Space & add(const wchar_t * field, unsigned long long val); Space & add(const wchar_t * field, float val); Space & add(const wchar_t * field, double val); Space & add(const wchar_t * field, const char * val); Space & add(const wchar_t * field, const wchar_t * val); Space & add(const wchar_t * field, const std::string & val); Space & add(const wchar_t * field, const std::wstring & val); Space & add(const wchar_t * field, const Space * space); Space & add_empty_space(const wchar_t * field); // IMPROVEME rename me to something better Space & add_child_space(const wchar_t * space_name); bool is_null() const; bool is_bool() const; bool is_llong() const; bool is_long_long() const; bool is_float() const; bool is_double() const; bool is_numeric() const; bool is_str() const; bool is_wstr() const; bool is_text() const; bool is_object() const; bool is_table() const; bool to_bool() const; short to_short() const; int to_int() const; long to_long() const; long long to_llong() const; long long to_long_long() const; unsigned short to_ushort() const; unsigned int to_uint() const; unsigned long to_ulong() const; unsigned long long to_ullong() const; unsigned long long to_ulong_long() const; std::string to_str() const; std::wstring to_wstr() const; bool * get_bool(); long long * get_llong(); long long * get_long_long(); float * get_float(); double * get_double(); std::string * get_string(); std::wstring * get_wstring(); ObjectType * get_object(); TableType * get_table(); const bool * get_bool() const; const long long * get_llong() const; const long long * get_long_long() const; const float * get_float() const; const double * get_double() const; const std::string * get_string() const; const std::wstring * get_wstring() const; const ObjectType * get_object() const; const TableType * get_table() const; std::string serialize_to_space_str(bool pretty_print = false) const; std::wstring serialize_to_space_wstr(bool pretty_print = false) const; void serialize_to_space_to(std::string & str, bool pretty_print = false) const; void serialize_to_space_to(std::wstring & str, bool pretty_print = false) const; template void serialize_to_space_stream(StreamType & str, bool pretty_print = false, bool is_main_object = true) const { switch(type) { case type_null: serialize_space_null(str); break; case type_bool: serialize_space_bool(str); break; case type_long: serialize_space_long(str); break; case type_float: serialize_space_float(str); break; case type_double: serialize_space_double(str); break; case type_string: serialize_space_string(str); break; case type_wstring: serialize_space_wstring(str); break; case type_object: serialize_space_object(str, pretty_print, is_main_object); break; case type_table: serialize_space_table(str, pretty_print); break; } } std::string serialize_to_json_str() const; std::wstring serialize_to_json_wstr() const; void serialize_to_json_to(std::string & str) const; void serialize_to_json_to(std::wstring & str) const; template void serialize_to_json_stream(StreamType & str) const { switch(type) { case type_null: serialize_json_null(str); break; case type_bool: serialize_json_bool(str); break; case type_long: serialize_json_long(str); break; case type_float: serialize_json_float(str); break; case type_double: serialize_json_double(str); break; case type_string: serialize_json_string(str); break; case type_wstring: serialize_json_wstring(str); break; case type_object: serialize_json_object(str); break; case type_table: serialize_json_table(str); break; } } protected: template Space & add_generic(ArgType val) { initialize_value_table_if_needed(); Space * new_space = new Space(val); value.value_table.push_back(new_space); return *value.value_table.back(); } template Space & add_generic(const wchar_t * field, ArgType val) { initialize_value_object_if_needed(); auto insert_res = value.value_object.insert(std::make_pair(field, nullptr)); insert_res.first->second = new Space(val); return *(insert_res.first->second); } template ArgType to_generic_numeric_signed_value() const { long long val = to_long_long(); if( val < std::numeric_limits::min() || val > std::numeric_limits::max() ) val = 0; return val; } template ArgType to_generic_numeric_unsigned_value() const { unsigned long long val = to_ulong_long(); if( val > std::numeric_limits::max() ) val = 0; return val; } long long convert_string_to_long_long() const; long long convert_wstring_to_long_long() const; unsigned long long convert_string_to_ulong_long() const; unsigned long long convert_wstring_to_ulong_long() const; template void escape_to_space_format(int c, StreamType & out) const { // IMPLEMENT ME escape_to_json_format(c, out); } template void escape_to_json_format(int c, StreamType & out) const { switch(c) { case 0: out << '\\'; out << '0'; break; case '\r': out << '\\'; out << 'r'; break; case '\n': out << '\\'; out << 'n'; break; case '\\': out << '\\'; out << '\\'; break; case '"': out << '\\'; out << '\"'; break; //case '(': out << '\\'; out << '('; break; //case ')': out << '\\'; out << ')'; break; //case '=': out << '\\'; out << '='; break; default: out << static_cast(c); } } template void copy_input_string_to_output(const CharT * input_str, StreamType & out_str, Escape escape) const { while( *input_str ) { if( escape == Escape::no_escape ) out_str << static_cast(*input_str); else if( escape == Escape::escape_space ) escape_to_space_format(*input_str, out_str); else if( escape == Escape::escape_json ) escape_to_json_format(*input_str, out_str); input_str += 1; } } template void copy_input_stream_to_output(const StreamType & input_str, StreamType & out_str, Escape escape) const { typename StreamType::const_iterator i = input_str.begin(); while( i != input_str.end() ) { if( escape == Escape::no_escape ) out_str << static_cast(*i); else if( escape == Escape::escape_space ) escape_to_space_format(*i, out_str); else if( escape == Escape::escape_json ) escape_to_json_format(*i, out_str); ++i; } } template void serialize_string_buffer(const char * input_str, StreamType & out_str, Escape escape) const { if constexpr ( sizeof(char) == sizeof(typename StreamType::char_type) ) { // input and output are char (we assume it is utf8) copy_input_string_to_output(input_str, out_str, escape); } else { StreamType temp_stream; // input is utf8 but output is wide UTF8ToWide(input_str, temp_stream, false); copy_input_stream_to_output(temp_stream, out_str, escape); } } template void serialize_string_buffer(const wchar_t * input_str, StreamType & out_str, Escape escape) const { if constexpr ( sizeof(wchar_t) == sizeof(typename StreamType::char_type) ) { // input and output are wide characters copy_input_string_to_output(input_str, out_str, escape); } else { StreamType temp_stream; // input is wide but output is utf8 WideToUTF8(input_str, temp_stream, false); copy_input_stream_to_output(temp_stream, out_str, escape); } } template void serialize_space_null(StreamType & str) const { serialize_string_buffer(L"null", str, Escape::escape_space); } template void serialize_space_bool(StreamType & str) const { if( value.value_bool ) { serialize_string_buffer(L"true", str, Escape::escape_space); } else { serialize_string_buffer(L"false", str, Escape::escape_space); } } template void serialize_space_long(StreamType & str) const { wchar_t buffer[50]; size_t buffer_len = sizeof(buffer) / sizeof(char); PT::Toa(value.value_long, buffer, buffer_len); serialize_string_buffer(buffer, str, Escape::escape_space); } template void serialize_space_float(StreamType & str) const { wchar_t buffer[100]; size_t buffer_len = sizeof(buffer) / sizeof(char); int chars_written = std::swprintf(buffer, buffer_len, L"%e", static_cast(value.value_float)); if( errno == EOVERFLOW || chars_written < 0 ) buffer[0] = 0; serialize_string_buffer(buffer, str, Escape::escape_space); } template void serialize_space_double(StreamType & str) const { wchar_t buffer[100]; size_t buffer_len = sizeof(buffer) / sizeof(wchar_t); int chars_written = std::swprintf(buffer, buffer_len, L"%e", value.value_double); if( errno == EOVERFLOW || chars_written < 0 ) buffer[0] = 0; serialize_string_buffer(buffer, str, Escape::escape_space); } template void serialize_space_string(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_string.c_str(), str, Escape::escape_space); str << '"'; } template void serialize_space_wstring(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_wstring.c_str(), str, Escape::escape_space); str << '"'; } template void serialize_space_object(StreamType & str, bool pretty_print, bool is_main_object) const { if( !is_main_object ) { str << '{'; print_if(pretty_print && (!value.value_object.empty() || (child_spaces && !child_spaces->empty())), str, '\n'); } bool is_first = true; for(auto & map_item : value.value_object) { if( !is_first ) print_if(pretty_print, str, '\n', ','); bool quote_field = should_field_be_quoted(map_item.first); print_if(quote_field, str, '"'); serialize_string_buffer(map_item.first.c_str(), str, Escape::escape_space); print_if(quote_field, str, '"'); print_if(pretty_print, str, ' '); str << '='; print_if(pretty_print, str, ' '); map_item.second->serialize_to_space_stream(str, pretty_print, false); is_first = false; } print_if(!is_first && pretty_print, str, '\n'); serialize_child_spaces(str, pretty_print); if( !is_main_object ) { str << '}'; print_if(pretty_print, str, '\n'); } } template void serialize_child_spaces(StreamType & str, bool pretty_print) const { if( child_spaces && !child_spaces->empty() ) { print_if(pretty_print, str, '\n'); for(Space * child_space : *child_spaces) { print_if(!pretty_print, str, ' '); if( child_space->name && !child_space->name->empty() ) { bool quote_field = should_field_be_quoted(*child_space->name); print_if(quote_field, str, '"'); serialize_string_buffer(child_space->name->c_str(), str, Escape::escape_space); print_if(quote_field, str, '"'); str << ' '; } child_space->serialize_to_space_stream(str, pretty_print, false); print_if(pretty_print, str, '\n'); } } } template void serialize_space_table(StreamType & str, bool pretty_print) const { bool multivalue_table = false; bool is_first = true; if( value.value_table.size() > 1 ) { multivalue_table = true; } str << '('; print_if(pretty_print && multivalue_table, str, '\n'); for(Space * space : value.value_table) { if( !is_first ) print_if(pretty_print, str, '\n', ','); space->serialize_to_space_stream(str, pretty_print, false); is_first = false; } print_if(pretty_print && multivalue_table, str, '\n'); str << ')'; } template void serialize_json_null(StreamType & str) const { serialize_string_buffer(L"null", str, Escape::escape_json); } template void serialize_json_bool(StreamType & str) const { if( value.value_bool ) { serialize_string_buffer(L"true", str, Escape::escape_json); } else { serialize_string_buffer(L"false", str, Escape::escape_json); } } template void serialize_json_long(StreamType & str) const { wchar_t buffer[50]; size_t buffer_len = sizeof(buffer) / sizeof(char); PT::Toa(value.value_long, buffer, buffer_len); serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_float(StreamType & str) const { wchar_t buffer[100]; size_t buffer_len = sizeof(buffer) / sizeof(char); int chars_written = std::swprintf(buffer, buffer_len, L"%e", static_cast(value.value_float)); if( errno == EOVERFLOW || chars_written < 0 ) buffer[0] = 0; serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_double(StreamType & str) const { wchar_t buffer[100]; size_t buffer_len = sizeof(buffer) / sizeof(wchar_t); int chars_written = std::swprintf(buffer, buffer_len, L"%e", value.value_double); if( errno == EOVERFLOW || chars_written < 0 ) buffer[0] = 0; serialize_string_buffer(buffer, str, Escape::escape_json); } template void serialize_json_string(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_string.c_str(), str, Escape::escape_json); str << '"'; } template void serialize_json_wstring(StreamType & str) const { str << '"'; serialize_string_buffer(value.value_wstring.c_str(), str, Escape::escape_json); str << '"'; } template void serialize_json_object(StreamType & str) const { str << '{'; bool is_first = true; for(auto & map_item : value.value_object) { if( !is_first ) { str << ','; } str << '"'; serialize_string_buffer(map_item.first.c_str(), str, Escape::escape_json); str << '"'; str << ':'; map_item.second->serialize_to_json_stream(str); is_first = false; } str << '}'; } template void serialize_json_table(StreamType & str) const { str << '['; bool is_first = true; for(Space * space : value.value_table) { if( !is_first ) { str << ','; } space->serialize_to_json_stream(str); is_first = false; } str << ']'; } public: ///////////////////////////// remove me template void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const { } template void SerializeTableMulti(Stream & out, bool use_indents, int level) const { } template static void PrintValue(Stream & out, const StringType & str, bool use_quote = true) { } template static void PrintKey(Stream & out, const std::wstring & str) { } template static void PrintLevel(Stream & out, bool use_indents, int level) { } ///////////////////////////// protected: template bool should_field_be_quoted(StringType & str) const { if( str.empty() ) return true; for(size_t i = 0 ; i < str.size() ; ++i) { int c = str[i]; // '{' is used when child spaces begin if( c == '\n' || c == '#' || c == '=' || c == '{' || c == '}' ) return true; } return false; } template void print_if(bool condition, StreamType & str, wchar_t c) const { if( condition ) str << c; } template void print_if(bool condition, StreamType & str, wchar_t c1, wchar_t c2) const { if( condition ) str << c1; else str << c2; } void copy_value_from(const Space & space); void copy_child_spaces_from(const Space & space); void copy_space_name_from(const Space & space); void copy_from(const Space & space); void copy_value_object(const Value & value_from); void copy_value_table(const Value & value_from); void initialize_value_null_if_needed(); void initialize_value_bool_if_needed(); void initialize_value_long_if_needed(); void initialize_value_float_if_needed(); void initialize_value_double_if_needed(); void initialize_value_string_if_needed(); void initialize_value_wstring_if_needed(); void initialize_value_object_if_needed(); void initialize_value_table_if_needed(); void initialize_child_spaces_if_needed(); void initialize_space_name_if_needed(); void remove_value(); void remove_value_string(); void remove_value_wstring(); void remove_value_object(); void remove_value_table(); void remove_child_spaces(); void remove_space_name(); }; } // namespace #endif