start working on a new version of Space struct - better support for JSON format

now we have a correct model, some methods for setting/getting values and serialization to json
(no serialization to Space yet)
pull/4/head
Tomasz Sowa 2 years ago
parent 7ccd435e2c
commit f65d934e8c

File diff suppressed because it is too large Load Diff

@ -5,7 +5,7 @@
*/
/*
* Copyright (c) 2010-2018, Tomasz Sowa
* Copyright (c) 2010-2021, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -41,7 +41,11 @@
#include <string>
#include <vector>
#include <map>
#include <cstdio>
#include <cwchar>
#include "textstream/types.h"
#include "convert/inttostr.h"
//#include "utf8/utf8.h"
@ -165,400 +169,625 @@ class Space
{
public:
typedef std::map<std::wstring, Space*> ObjectType;
typedef std::vector<Space*> TableType;
/*
this is the table which represents your config file
in the Table map: the first (key) is your 'option' and the second is 'list'
*/
typedef std::vector<std::wstring> Value;
typedef std::map<std::wstring, Value> Table;
Space();
~Space();
Space(const Space & s);
Space & operator=(const Space & s);
// IMPROVE ME
// add move cctor
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()
{
}
void Clear();
~Value()
{
}
};
Type type;
Value value;
/*
returns true if such an option has 'value'
useful when testing lists (they don't have to be copied out)
*/
bool HasValue(const wchar_t * name, const wchar_t * value);
bool HasValue(const wchar_t * name, const std::wstring & value);
bool HasValue(const std::wstring & name, const wchar_t * value);
bool HasValue(const std::wstring & name, const std::wstring & value);
Space();
Space(const Space & space);
Space & operator=(const Space & space);
// add move cctor
~Space();
/*
*
* methods for getting/finding a value
*
*
*/
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);
// set a new value
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 = nullptr);
// 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
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() const;
std::wstring serialize_to_space_wstr() const;
void serialize_to_space_to(std::string & str) const;
void serialize_to_space_to(std::wstring & str) const;
template<typename StreamType>
void serialize_to_space_stream(StreamType & str) const
{
// IMPROVEME
/*
*
* their working in O(log)
* can return a null pointer
*
*/
Value * GetValue(const wchar_t * name);
Value * GetValue(const std::wstring & name);
const Value * GetValue(const wchar_t * name) const;
const Value * GetValue(const std::wstring & name) const;
switch(type)
{
case type_null:
//serialize_null(str, escape);
break;
case type_bool:
//serialize_bool(str, escape);
break;
case type_long:
//serialize_long(str, escape);
break;
// O(n) complexity
Value * GetValueNoCase(const wchar_t * name);
Value * GetValueNoCase(const std::wstring & name);
const Value * GetValueNoCase(const wchar_t * name) const;
const Value * GetValueNoCase(const std::wstring & name) const;
case type_float:
//serialize_float(str, escape);
break;
case type_double:
//serialize_double(str, escape);
break;
// they can return a null pointer if there is not such a 'name'
std::wstring * GetFirstValue(const wchar_t * name);
std::wstring * GetFirstValue(const std::wstring & name);
case type_string:
//serialize_string(str, escape);
break;
const std::wstring * GetFirstValue(const wchar_t * name) const;
const std::wstring * GetFirstValue(const std::wstring & name) const;
case type_wstring:
//serialize_wstring(str, escape);
break;
case type_object:
//serialize_object(str, escape);
break;
/*
those methods are used to extract information from space.table
as a parameter they take the name of an option
and a default value (if there is no such a parameter),
they return appropriate value (either text, int or boolean)
(in lists they return the first item if exists)
case type_table:
//serialize_table(str, escape);
break;
}
}
when calling Text(...) and AText(...) you should copy the object to whom a reference is returned
it will be cleared in a next call to one of these methods (as well to Int() Size() and Bool())
AText(...) always returns a reference to UTF-8 string
*/
//std::wstring Text(const wchar_t * name) const;
std::wstring Text(const wchar_t * name, const wchar_t * def = 0) const;
std::wstring Text(const std::wstring & name, const wchar_t * def = 0) const;
std::wstring Text(const std::wstring & name, const std::wstring & def) const;
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;
// returns a reference
// if there is no such an option then a new one (def value) is inserted
//std::wstring & TextRef(const wchar_t * name);
std::wstring & TextRef(const wchar_t * name, const wchar_t * def = 0);
std::wstring & TextRef(const std::wstring & name, const wchar_t * def = 0);
std::wstring & TextRef(const std::wstring & name, const std::wstring & def);
// returns UTF-8 string
//std::string TextA(const wchar_t * name) const;
std::string TextA(const wchar_t * name, const char * def) const;
std::string TextA(const std::wstring & name, const char * def) const;
std::string TextA(const std::wstring & name, const std::string & def) const;
template<typename StreamType>
void serialize_to_json_stream(StreamType & str) const
{
switch(type)
{
case type_null:
serialize_json_null(str);
break;
std::string TextA(const wchar_t * name, const wchar_t * def) const;
std::string TextA(const std::wstring & name, const wchar_t * def) const;
std::string TextA(const std::wstring & name, const std::wstring & def) const;
case type_bool:
serialize_json_bool(str);
break;
case type_long:
serialize_json_long(str);
break;
case type_float:
serialize_json_float(str);
break;
int Int(const wchar_t * name, int def = 0) const;
int Int(const std::wstring & name, int def = 0) const;
unsigned int UInt(const wchar_t * name, unsigned int def = 0) const;
unsigned int UInt(const std::wstring & name, unsigned int def = 0) const;
case type_double:
serialize_json_double(str);
break;
long Long(const wchar_t * name, long def = 0) const;
long Long(const std::wstring & name, long def = 0) const;
unsigned long ULong(const wchar_t * name, unsigned long def = 0) const;
unsigned long ULong(const std::wstring & name, unsigned long def = 0) const;
case type_string:
serialize_json_string(str);
break;
long long LongLong(const wchar_t * name, long long def = 0) const;
long long LongLong(const std::wstring & name, long long def = 0) const;
unsigned long long ULongLong(const wchar_t * name, unsigned long long def = 0) const;
unsigned long long ULongLong(const std::wstring & name, unsigned long long def = 0) const;
case type_wstring:
serialize_json_wstring(str);
break;
size_t Size(const wchar_t * name, size_t def = 0) const;
size_t Size(const std::wstring & name, size_t def = 0) const;
case type_object:
serialize_json_object(str);
break;
bool Bool(const wchar_t * name, bool def = false) const;
bool Bool(const std::wstring & name, bool def = false) const;
case type_table:
serialize_json_table(str);
break;
}
}
protected:
/*
*
* methods for adding a new value
*
*
*/
template<typename ArgType>
Space & add_generic(ArgType val)
{
initialize_value_table_if_needed();
std::wstring & FindAdd(const wchar_t * name);
std::wstring & FindAdd(const std::wstring & name);
std::wstring & FindAdd(const WTextStream & name);
Space * new_space = new Space(val);
value.value_table.push_back(new_space);
std::wstring & Add(const wchar_t * name, bool value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, bool value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, int value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, int value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, long value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, long value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, size_t value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, size_t value, bool replace_existing = true);
return *value.value_table.back();
}
std::wstring & Add(const std::wstring & name, const std::wstring & value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, const wchar_t * value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const wchar_t * value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const std::wstring & value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const WTextStream & value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, const WTextStream & value, bool replace_existing = true);
std::wstring & Add(const WTextStream & name, const WTextStream & value, bool replace_existing = true);
template<typename ArgType>
Space & add_generic(const wchar_t * field, ArgType val)
{
initialize_value_object_if_needed();
void Remove(const wchar_t * name);
void Remove(const std::wstring & name);
auto insert_res = value.value_object.insert(std::make_pair(field, nullptr));
insert_res.first->second = new Space(val);
return *(insert_res.first->second);
}
Space & AddSpace(const wchar_t * name);
Space & AddSpace(const std::wstring & name);
template<typename ArgType>
ArgType to_generic_numeric_signed_value() const
{
long long val = to_long_long();
// looking for the first space with the specified name
// if there is not such a space those methods return a null pointer
Space * FindSpace(const wchar_t * name);
Space * FindSpace(const std::wstring & name);
if( val < std::numeric_limits<ArgType>::min() || val > std::numeric_limits<ArgType>::max() )
val = 0;
// looking for the first space with the specified name
// if there is not such a space then this methods adds such a space
Space & FindAddSpace(const wchar_t * name);
Space & FindAddSpace(const std::wstring & name);
return val;
}
void RemoveSpace(const wchar_t * name);
void RemoveSpace(const std::wstring & name);
void RemoveSpace(size_t child_index);
template<typename ArgType>
ArgType to_generic_numeric_unsigned_value() const
{
unsigned long long val = to_ulong_long();
if( val > std::numeric_limits<ArgType>::max() )
val = 0;
/*
*
*
raw access to the parsed values
*
*
*/
return val;
}
std::wstring name; // space name
Table table; // std::map<std::wstring, std::vector<std::wstring> >
long long convert_string_to_long_long() const;
long long convert_wstring_to_long_long() const;
// childs
typedef std::vector<Space*> Spaces;
Spaces spaces;
unsigned long long convert_string_to_ulong_long() const;
unsigned long long convert_wstring_to_ulong_long() const;
// a parent space
// null means a root space
Space * parent;
/*
those methods are used to extract lists
return true if such an option exists (but value can be an empty list)
*/
bool ListText(const wchar_t * name, std::vector<std::wstring> & list);
bool ListText(const std::wstring & name, std::vector<std::wstring> & list);
template<typename StreamType>
void escape_to_space_format(int c, StreamType & out) const
{
// IMPLEMENT ME
}
/*
serialize the content
*/
template<class Stream>
void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const;
template<typename StreamType>
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<typename StreamType::char_type>(c);
}
}
template<class Stream>
void SerializeTableMulti(Stream & out, bool use_indents, int level) const;
template<class Stream, class StringType>
static void PrintValue(Stream & out, const StringType & str, bool use_quote = true);
template<class Stream>
static void PrintKey(Stream & out, const std::wstring & str);
template<typename CharT, typename StreamType>
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<typename StreamType::char_type>(*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);
template<class Stream>
static void PrintLevel(Stream & out, bool use_indents, int level);
input_str += 1;
}
}
template<typename StreamType>
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<typename StreamType::char_type>(*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);
private:
++i;
}
}
mutable std::wstring tmp_name;
std::wstring tmp_value;
std::wstring tmp_value_text;
std::string tmp_value_text_ascii;
template<typename StreamType>
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;
static unsigned int ToUInt(const std::wstring & value);
static int ToInt(const std::wstring & value);
static unsigned long ToULong(const std::wstring & value);
static long ToLong(const std::wstring & value);
static unsigned long long ToULongLong(const std::wstring & value);
static long long ToLongLong(const std::wstring & value);
static size_t ToSize(const std::wstring & value);
static bool ToBool(const std::wstring & value);
// input is utf8 but output is wide
UTF8ToWide(input_str, temp_stream, false);
copy_input_stream_to_output(temp_stream, out_str, escape);
}
}
static bool IsWhite(int c);
static bool HasWhite(const std::wstring & str);
};
template<typename StreamType>
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<class Stream>
void Space::PrintLevel(Stream & out, bool use_indents, int level)
{
if( use_indents )
template<typename StreamType>
void serialize_json_null(StreamType & str) const
{
for(int i=0 ; i<level ; ++i)
out << ' ';
serialize_string_buffer(L"null", str, Escape::escape_json);
}
}
template<class Stream, class StringType>
void Space::PrintValue(Stream & out, const StringType & str, bool use_quote)
{
if( use_quote )
out << '\"';
for(size_t i=0 ; i<str.size() ; ++i)
template<typename StreamType>
void serialize_json_bool(StreamType & str) const
{
switch(str[i])
if( value.value_bool )
{
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 << str[i];
serialize_string_buffer(L"true", str, Escape::escape_json);
}
else
{
serialize_string_buffer(L"false", str, Escape::escape_json);
}
}
if( use_quote )
out << '\"';
}
template<typename StreamType>
void serialize_json_long(StreamType & str) const
{
wchar_t buffer[50];
size_t buffer_len = sizeof(buffer) / sizeof(char);
template<class Stream>
void Space::PrintKey(Stream & out, const std::wstring & str)
{
bool use_quote = false;
PT::Toa(value.value_long, buffer, buffer_len);
serialize_string_buffer(buffer, str, Escape::escape_json);
}
// CHECK ME
// HasWhite doesn't take a new line into account, is it correct to use it here?
if( str.empty() || HasWhite(str) )
use_quote = true;
template<typename StreamType>
void serialize_json_float(StreamType & str) const
{
wchar_t buffer[50];
size_t buffer_len = sizeof(buffer) / sizeof(char);
PrintValue(out, str, use_quote);
}
std::swprintf(buffer, buffer_len, L"%f", value.value_float);
serialize_string_buffer(buffer, str, Escape::escape_json);
}
template<typename StreamType>
void serialize_json_double(StreamType & str) const
{
wchar_t buffer[50];
size_t buffer_len = sizeof(buffer) / sizeof(char);
std::swprintf(buffer, buffer_len, L"%f", value.value_double);
serialize_string_buffer(buffer, str, Escape::escape_json);
}
template<typename StreamType>
void serialize_json_string(StreamType & str) const
{
str << '"';
serialize_string_buffer(value.value_string.c_str(), str, Escape::escape_json);
str << '"';
}
template<class Stream>
void Space::SerializeTableMulti(Stream & out, bool use_indents, int level) const
{
Table::const_iterator i2;
size_t v;
template<typename StreamType>
void serialize_json_wstring(StreamType & str) const
{
str << '"';
serialize_string_buffer(value.value_wstring.c_str(), str, Escape::escape_json);
str << '"';
}
for(i2 = table.begin() ; i2 != table.end() ; ++i2)
template<typename StreamType>
void serialize_json_object(StreamType & str) const
{
PrintLevel(out, use_indents, level);
PrintKey(out, i2->first);
out << L" = ";
str << '{';
if( i2->second.size() != 1 )
out << '(';
bool is_first = true;
for(v = 0 ; v < i2->second.size() ; ++v)
for(auto & map_item : value.value_object)
{
if( v > 0 )
PrintLevel(out, use_indents, level + i2->first.size() + 3);
PrintValue(out, i2->second[v]);
if( v + 1 < i2->second.size() )
out << '\n';
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;
}
if( i2->second.size() != 1 )
out << ')';
out << '\n';
str << '}';
}
}
template<class Stream>
void Space::Serialize(Stream & out, bool use_indents, bool use_comments, int level) const
{
if( level > 0 )
template<typename StreamType>
void serialize_json_table(StreamType & str) const
{
out << '\n';
PrintLevel(out, use_indents, level);
str << '[';
if( !name.empty() )
{
PrintKey(out, name);
out << ' ';
}
bool is_first = true;
out << L"(\n";
if( use_comments )
for(Space * space : value.value_table)
{
PrintLevel(out, use_indents, level);
out << L"# space level " << level << '\n';
if( !is_first )
{
str << ',';
}
space->serialize_to_json_stream(str);
is_first = false;
}
str << ']';
}
SerializeTableMulti(out, use_indents, level);
for(size_t i=0 ; i<spaces.size() ; ++i)
spaces[i]->Serialize(out, use_indents, use_comments, level+1);
if( level > 0 )
public:
///////////////////////////// remove me
template<class Stream>
void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const
{
PrintLevel(out, use_indents, level);
out << ')';
}
if( use_comments )
{
if( name.empty() )
out << L" # end of unnamed space";
else
out << L" # end of space: " << name;
template<class Stream>
void SerializeTableMulti(Stream & out, bool use_indents, int level) const
{
}
out << L" (level " << level << L")";
}
template<class Stream, class StringType>
static void PrintValue(Stream & out, const StringType & str, bool use_quote = true)
{
}
template<class Stream>
static void PrintKey(Stream & out, const std::wstring & str)
{
}
out << '\n';
template<class Stream>
static void PrintLevel(Stream & out, bool use_indents, int level)
{
}
}
/////////////////////////////
protected:
void copy_value_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 remove_value();
void remove_value_string();
void remove_value_wstring();
void remove_value_object();
void remove_value_table();
};
} // namespace

Loading…
Cancel
Save