From cf377204a9df2584990c469b1befb4a95607540f Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Sun, 26 Feb 2023 22:19:46 +0100 Subject: [PATCH] improve the xml serializer while here: - do some refactoring in BaseExpression --- Makefile | 9 ++ src/baseexpression.cpp | 15 +++ src/baseexpression.h | 249 +++++++++++++++++++++-------------------- src/morm.h | 4 +- src/xmlconnector.cpp | 105 ++++++++++++++++- src/xmlconnector.h | 22 +++- src/xmlexpression.cpp | 96 ++++++---------- src/xmlexpression.h | 23 ++-- 8 files changed, 324 insertions(+), 199 deletions(-) diff --git a/Makefile b/Makefile index 57e66b8..86c2904 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,20 @@ samples: FORCE $(MAKE) -C samples +samples-gcc11: FORCE + env CXX=g++11 CXXFLAGS="-Wl,-rpath=/usr/local/lib/gcc11/ -Wall -pedantic -O0 -g -std=c++20 -fmax-errors=1 -I../src -I../../pikotools/src -I/usr/local/include" $(MAKE) -C src + env CXX=g++11 CXXFLAGS="-Wl,-rpath=/usr/local/lib/gcc11/ -Wall -pedantic -O0 -g -std=c++20 -fmax-errors=1 -I../src -I../../pikotools/src -I/usr/local/include" $(MAKE) -C samples + + clean: FORCE $(MAKE) -C src clean $(MAKE) -C samples clean +cleanall: clean + $(MAKE) -C ../pikotools clean + + depend: FORCE $(MAKE) -C src depend $(MAKE) -C samples depend diff --git a/src/baseexpression.cpp b/src/baseexpression.cpp index 27adbc8..34c44af 100644 --- a/src/baseexpression.cpp +++ b/src/baseexpression.cpp @@ -241,6 +241,21 @@ void BaseExpression::put_field_name(const wchar_t * field_name, const FT & field } +void BaseExpression::put_field_closing_name(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env) +{ + put_field_name(field_name, field_type, model_env); +} + + +void BaseExpression::put_value_list_opening_index(size_t index, const FT & field_type) +{ +} + + +void BaseExpression::put_value_list_closing_index(size_t index, const FT & field_type) +{ +} + void BaseExpression::save_foreign_key(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env) { diff --git a/src/baseexpression.h b/src/baseexpression.h index 0e33ec9..28383f1 100644 --- a/src/baseexpression.h +++ b/src/baseexpression.h @@ -122,48 +122,12 @@ public: else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - if( model_env && model_env->set_field_name_helper ) - { - if( (size_t)model_env->field_index < model_env->set_field_name_helper->size() ) - { - put_field_name_and_table_if_needed((*model_env->set_field_name_helper)[model_env->field_index], field_type, model_env); - put_name_value_separator(); - put_field_value_or_null(field_value, getter_method, field_type, model_env); - } - - model_env->field_index += 1; - } - else - { - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - put_field_value_or_null(field_value, getter_method, field_type, model_env); - } + put_field_name_and_value(field_name, field_value, getter_method, field_type, model_env); } else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS ) { - (*out_stream) << '\n'; - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - put_field_value_or_null(field_value, getter_method, field_type, model_env); - put_name_value_separator(); - /* - * IMPROVEME currently only used in XML serializer so we put / directly here - * we need some virtual methods for it - */ - before_field_name(); // here should be a different method such as before_closing_field_name() - - if( out_stream ) - { - (*out_stream) << '/'; - } - - esc(field_name, *out_stream, FT::default_type, nullptr); /* do not use provided field_type here - it would use e.g. binary mode if it was set, similar don't use model_env */ - after_field_name(); // here too, e.g. after_closing_field_name() - /* - * - */ + put_field_name_and_value_and_closing_name(field_name, field_value, getter_method, field_type, model_env); } field_after(); @@ -171,30 +135,6 @@ public: } - template - void put_field_value_or_null(const FieldValue & field_value, void (Model::*getter_method)(pt::Stream &), const FT & field_type, ModelEnv * model_env) - { - if( getter_method ) - { - put_field_value(getter_method, field_type, model_env); - } - else - { - if( field_type.is_primary_key() ) - { - if( model_env && model_env->has_primary_key_set ) - put_field_value(field_value, field_type, model_env); - else - put_null_value(); - } - else - { - put_field_value(field_value, field_type, model_env); - } - } - } - - template void field_in(pt::TextStream & stream, const wchar_t * field_name, const std::set & container, ModelEnv * model_env) { @@ -231,34 +171,12 @@ public: // else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - put_field_value_list(field_value, model_container_type, field_type, model_connector, model_env, foo); + put_field_name_and_value_list(field_name, field_value, model_container_type, field_type, model_connector, model_env, foo); } else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS ) { - (*out_stream) << '\n'; - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - put_field_value_list(field_value, model_container_type, field_type, model_connector, model_env, foo); - put_name_value_separator(); - /* - * IMPROVEME currently only used in XML serializer so we put / directly here - * we need some virtual methods for it - */ - before_field_name(); // here should be a different method such as before_closing_field_name() - - if( out_stream ) - { - (*out_stream) << '/'; - } - - esc(field_name, *out_stream, FT::default_type, nullptr); /* do not use provided field_type here - it would use e.g. binary mode if it was set, similar don't use model_env */ - after_field_name(); // here too, e.g. after_closing_field_name() - /* - * - */ + put_field_name_and_value_list_and_closing_name(field_name, field_value, model_container_type, field_type, model_connector, model_env, foo); } field_after(); @@ -284,41 +202,19 @@ public: else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - generate_from_model(field_model, field_type); + put_field_name_and_value_model(field_name, field_model, field_type, model_env); } else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS ) { - (*out_stream) << '\n'; - put_field_name_and_table_if_needed(field_name, field_type, model_env); - put_name_value_separator(); - generate_from_model(field_model, field_type); - put_name_value_separator(); - /* - * IMPROVEME currently only used in XML serializer so we put / directly here - * we need some virtual methods for it - */ - before_field_name(); // here should be a different method such as before_closing_field_name() - - if( out_stream ) - { - (*out_stream) << '/'; - } - - esc(field_name, *out_stream, FT::default_type, nullptr); /* do not use provided field_type here - it would use e.g. binary mode if it was set, similar don't use model_env */ - after_field_name(); // here too, e.g. after_closing_field_name() - (*out_stream) << '\n'; - /* - * - */ + put_field_name_and_value_model_and_closing_name(field_name, field_model, field_type, model_env); } field_after(); } } + template void field_to_stream(pt::TextStream & stream, const wchar_t * field_name, const FieldValue & field_value, const FT & field_type, ModelEnv * model_env) { @@ -465,6 +361,9 @@ protected: virtual void put_field_name_and_table_if_needed(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); virtual void put_field_name(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); + virtual void put_field_closing_name(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); + virtual void put_value_list_opening_index(size_t index, const FT & field_type); + virtual void put_value_list_closing_index(size_t index, const FT & field_type); virtual void save_foreign_key(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); virtual void dump_additional_info(Model & model); @@ -548,7 +447,7 @@ protected: } else { - put_field_value_list_non_model(field_value, model_connector); + put_field_value_list_non_model(field_value, model_connector, field_type); } } @@ -559,20 +458,22 @@ protected: { if( model_connector && model_env && out_stream ) { - bool is_first = true; before_field_value_list(); + size_t index = 0; for(auto & child_model_item : field_value) { if( can_field_model_be_generated(child_model_item.get_has_primary_key_set(), field_type) ) { - if( !is_first ) + if( index > 0 ) { field_value_list_separator(); } + put_value_list_opening_index(index, field_type); put_field_value_list_model(child_model_item, model_connector, model_env); - is_first = false; + put_value_list_closing_index(index, field_type); + index += 1; } } @@ -587,20 +488,22 @@ protected: { if( model_connector && model_env && out_stream ) { - bool is_first = true; before_field_value_list(); + size_t index = 0; for(auto * child_model_item : field_value) { if( can_field_model_be_generated(child_model_item->get_has_primary_key_set(), field_type) ) { - if( !is_first ) + if( index > 0 ) { field_value_list_separator(); } + put_value_list_opening_index(index, field_type); put_field_value_list_model(*child_model_item, model_connector, model_env); - is_first = false; + put_value_list_closing_index(index, field_type); + index += 1; } } @@ -626,22 +529,24 @@ protected: template - void put_field_value_list_non_model(ModelContainer & field_value, ModelConnector * model_connector) + void put_field_value_list_non_model(ModelContainer & field_value, ModelConnector * model_connector, const FT & field_type) { if( model_connector && out_stream ) { - bool is_first = true; before_field_value_list(); + size_t index = 0; for(const auto & m : field_value) { - if( !is_first ) + if( index > 0 ) { field_value_list_separator(); } + put_value_list_opening_index(index, field_type); put_field_value(m, FT::default_type); - is_first = false; + put_value_list_closing_index(index, field_type); + index += 1; } after_field_value_list(); @@ -846,6 +751,106 @@ protected: } + template + void put_field_value_or_null(const FieldValue & field_value, void (Model::*getter_method)(pt::Stream &), const FT & field_type, ModelEnv * model_env) + { + if( getter_method ) + { + put_field_value(getter_method, field_type, model_env); + } + else + { + if( field_type.is_primary_key() ) + { + if( model_env && model_env->has_primary_key_set ) + put_field_value(field_value, field_type, model_env); + else + put_null_value(); + } + else + { + put_field_value(field_value, field_type, model_env); + } + } + } + + + template + void put_field_name_and_value(const wchar_t * field_name, const FieldValue & field_value, void (Model::*getter_method)(pt::Stream &), const FT & field_type, ModelEnv * model_env) + { + if( model_env && model_env->set_field_name_helper ) + { + if( (size_t)model_env->field_index < model_env->set_field_name_helper->size() ) + { + put_field_name_and_table_if_needed((*model_env->set_field_name_helper)[model_env->field_index], field_type, model_env); + put_name_value_separator(); + put_field_value_or_null(field_value, getter_method, field_type, model_env); + } + + model_env->field_index += 1; + } + else + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + put_field_value_or_null(field_value, getter_method, field_type, model_env); + } + } + + + template + void put_field_name_and_value_list(const wchar_t * field_name, ModelContainer & field_value, ModelContainerType * model_container_type, + const FT & field_type, ModelConnector * model_connector, ModelEnv * model_env, IsContainerByValueRenameMe * foo) + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + put_field_value_list(field_value, model_container_type, field_type, model_connector, model_env, foo); + } + + + template + void put_field_name_and_value_model(const wchar_t * field_name, ModelClass & field_model, const FT & field_type, ModelEnv * model_env) + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + generate_from_model(field_model, field_type); + } + + + template + void put_field_name_and_value_and_closing_name(const wchar_t * field_name, const FieldValue & field_value, void (Model::*getter_method)(pt::Stream &), const FT & field_type, ModelEnv * model_env) + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + put_field_value_or_null(field_value, getter_method, field_type, model_env); + put_name_value_separator(); + put_field_closing_name(field_name, field_type, model_env); + } + + + template + void put_field_name_and_value_list_and_closing_name(const wchar_t * field_name, ModelContainer & field_value, ModelContainerType * model_container_type, + const FT & field_type, ModelConnector * model_connector, ModelEnv * model_env, IsContainerByValueRenameMe * foo) + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + put_field_value_list(field_value, model_container_type, field_type, model_connector, model_env, foo); + put_name_value_separator(); + put_field_closing_name(field_name, field_type, model_env); + } + + + template + void put_field_name_and_value_model_and_closing_name(const wchar_t * field_name, ModelClass & field_model, const FT & field_type, ModelEnv * model_env) + { + put_field_name_and_table_if_needed(field_name, field_type, model_env); + put_name_value_separator(); + generate_from_model(field_model, field_type); + put_name_value_separator(); + put_field_closing_name(field_name, field_type, model_env); + } + + }; } diff --git a/src/morm.h b/src/morm.h index 7097593..63be096 100644 --- a/src/morm.h +++ b/src/morm.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018-2022, Tomasz Sowa + * Copyright (c) 2018-2023, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,9 +43,11 @@ #include "jsonexpression.h" #include "postgresqlexpression.h" +#include "xmlexpression.h" #include "jsonconnector.h" #include "postgresqlconnector.h" +#include "xmlconnector.h" #include "modelconnector.h" #include "clearer.h" diff --git a/src/xmlconnector.cpp b/src/xmlconnector.cpp index 129077e..2330b21 100644 --- a/src/xmlconnector.cpp +++ b/src/xmlconnector.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2022, Tomasz Sowa + * Copyright (c) 2022-2023, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,6 +39,58 @@ namespace morm { + +XMLConnector::XMLConnector() +{ + put_doctype = true; + put_root_element = true; +} + + + +void XMLConnector::set_putting_doctype(bool put_doctype) +{ + this->put_doctype = put_doctype; +} + + +void XMLConnector::set_putting_root_element(bool put_root_element) +{ + this->put_root_element = put_root_element; +} + + +bool XMLConnector::get_putting_doctype() +{ + return put_doctype; +} + + +bool XMLConnector::get_putting_root_element() +{ + return put_root_element; +} + + +void XMLConnector::set_root_element(const wchar_t * root_name) +{ + root_element_name = root_name; +} + + +void XMLConnector::set_root_element(const std::wstring & root_name) +{ + root_element_name = root_name; +} + + +std::wstring & XMLConnector::get_root_element() +{ + return root_element_name; +} + + + void XMLConnector::allocate_default_expression() { deallocate_expression(); @@ -57,7 +109,58 @@ void XMLConnector::to_text(pt::TextStream & stream, Model & model) flat_expression->clear(); flat_expression->set_work_mode(MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS); flat_expression->allow_to_use_prefix(false); + + put_doctype_definition(stream); + put_opening_root_element(stream); flat_expression->generate_from_model(stream, model); + put_closing_root_element(stream); + } +} + + +void XMLConnector::put_doctype_definition(pt::TextStream & stream) +{ + if( put_doctype ) + { + stream << L""; + } +} + + +void XMLConnector::put_opening_root_element(pt::TextStream & stream) +{ + if( put_root_element ) + { + stream << L"<"; + put_root_element_name(stream); + stream << L">"; + } +} + + +void XMLConnector::put_closing_root_element(pt::TextStream & stream) +{ + if( put_root_element ) + { + stream << L""; + } +} + + +void XMLConnector::put_root_element_name(pt::TextStream & stream) +{ + if( root_element_name.empty() ) + { + stream << L"xml"; + } + else + { + if( flat_expression ) + { + flat_expression->esc(root_element_name, stream, FT::default_type, nullptr); + } } } diff --git a/src/xmlconnector.h b/src/xmlconnector.h index 340163e..00ba67b 100644 --- a/src/xmlconnector.h +++ b/src/xmlconnector.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2022, Tomasz Sowa + * Copyright (c) 2022-2023, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,13 +46,33 @@ class XMLConnector : public FlatConnector public: + XMLConnector(); + + virtual void set_putting_doctype(bool put_doctype); + virtual void set_putting_root_element(bool put_root_element); + virtual bool get_putting_doctype(); + virtual bool get_putting_root_element(); + + virtual void set_root_element(const wchar_t * root_name); + virtual void set_root_element(const std::wstring & root_name); + virtual std::wstring & get_root_element(); + void to_text(pt::TextStream & stream, Model & model); + protected: + bool put_doctype; + bool put_root_element; + std::wstring root_element_name; void allocate_default_expression(); + virtual void put_doctype_definition(pt::TextStream & stream); + virtual void put_opening_root_element(pt::TextStream & stream); + virtual void put_closing_root_element(pt::TextStream & stream); + virtual void put_root_element_name(pt::TextStream & stream); + }; } diff --git a/src/xmlexpression.cpp b/src/xmlexpression.cpp index f95dfcc..78b0111 100644 --- a/src/xmlexpression.cpp +++ b/src/xmlexpression.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2022, Tomasz Sowa + * Copyright (c) 2022-2023, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,46 +41,12 @@ namespace morm { -void XMLExpression::before_generate_from_model() -{ - BaseExpression::before_generate_from_model(); - - if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS ) - { - (*out_stream) << ""; - } -} - - -void XMLExpression::after_generate_from_model() -{ - BaseExpression::after_generate_from_model(); - - if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES_FIELDS ) - { - (*out_stream) << ""; - } -} - - - -void XMLExpression::field_before() -{ - BaseExpression::field_before(); - - if( !is_first_field ) - { - //(*out_stream) << ","; - } -} - - - void XMLExpression::before_field_name() { (*out_stream) << "<"; } + void XMLExpression::after_field_name() { (*out_stream) << ">"; @@ -88,17 +54,6 @@ void XMLExpression::after_field_name() -void XMLExpression::before_field_value_string(const FT & field_type, ModelEnv * model_env) -{ - //(*out_stream) << "\""; -} - -void XMLExpression::after_field_value_string(const FT & field_type, ModelEnv * model_env) -{ - //(*out_stream) << "\""; -} - - void XMLExpression::before_field_value(const pt::Space &, const FT & field_type, ModelEnv * model_env) { if( field_type.is_space() ) @@ -116,22 +71,25 @@ void XMLExpression::after_field_value(const pt::Space &, const FT & field_type, } +void XMLExpression::put_field_closing_name(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env) +{ + if( field_type.is_raw_field_name() ) + { + (*out_stream) << '/'; + (*out_stream) << field_name; + } + else + { + before_field_name(); + (*out_stream) << '/'; + esc(field_name, *out_stream, FT::default_type, nullptr); /* do not use provided field_type here - it would use e.g. binary mode if it was set, similar don't use model_env */ + after_field_name(); + } +} + void XMLExpression::put_name_value_separator() { - //(*out_stream) << ':'; -} - - -void XMLExpression::before_field_value_list() -{ - (*out_stream) << ""; -} - - -void XMLExpression::after_field_value_list() -{ - (*out_stream) << "
"; } @@ -164,4 +122,22 @@ void XMLExpression::esc(const pt::Space & space, pt::TextStream & stream, const } } + +void XMLExpression::put_value_list_opening_index(size_t index, const FT & field_type) +{ + (*out_stream) << L"<_" << index << L">"; +} + + +void XMLExpression::put_value_list_closing_index(size_t index, const FT & field_type) +{ + (*out_stream) << L""; +} + + +void XMLExpression::field_value_list_separator() +{ +} + + } diff --git a/src/xmlexpression.h b/src/xmlexpression.h index a713af5..8f9aaf7 100644 --- a/src/xmlexpression.h +++ b/src/xmlexpression.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2022, Tomasz Sowa + * Copyright (c) 2022-2023, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,8 +32,8 @@ * */ -#ifndef headerfile_morm_src_jsonexpression -#define headerfile_morm_src_jsonexpression +#ifndef headerfile_morm_src_xmlexpression +#define headerfile_morm_src_xmlexpression #include "flatexpression.h" @@ -47,32 +47,27 @@ class XMLExpression : public FlatExpression protected: - void before_generate_from_model(); - void after_generate_from_model(); - - - - void field_before(); + void put_field_closing_name(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); void before_field_name(); void after_field_name(); void put_name_value_separator(); - void before_field_value_list(); - void after_field_value_list(); - + using BaseExpression::esc; bool esc_char(wchar_t val, pt::TextStream & stream, const FT & field_type, ModelEnv * model_env); void esc(const pt::Space & space, pt::TextStream & stream, const FT & field_type, ModelEnv * model_env); private: - void before_field_value_string(const FT & field_type, ModelEnv * model_env); - void after_field_value_string(const FT & field_type, ModelEnv * model_env); void before_field_value(const pt::Space &, const FT & field_type, ModelEnv * model_env); void after_field_value(const pt::Space &, const FT & field_type, ModelEnv * model_env); + void put_value_list_opening_index(size_t index, const FT & field_type); + void put_value_list_closing_index(size_t index, const FT & field_type); + + void field_value_list_separator(); };